扑克牌
1.题目大意:给定n张扑克牌和这n张扑克牌的花色和大小(可能会有重复),需要输出还需要多少张牌才能凑齐一副扑克牌(这里不考虑大小王,所以共52张)。
2.思路概要:由于扑克牌会有重复,所以重要的操作就是字符串的处理和去重。这里先介绍下unique,它的作用是“去掉”容器中相邻元素的重复元素。(这里去掉要加一个引号,是因为实质上是一个伪去除,它会把重复的元素添加到容器末尾,而返回值是去重之后的尾地址。)这道题并不难,所以也不过多赘述。
3.代码实现:
#include<bits/stdc++.h>
using namespace std;
const int N=60;
int n;
string s[N];
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>s[i];
sort(s+1,s+1+n);
n=unique(s+1,s+1+n)-1-s;
cout<<52-n;
return 0;
}
地图探险
1.题干可能有点长,但不是很难理解。给定一个n*m的地图,地图中"."可以通过,"x"不能通过,机器人的初始坐标为(x0,y0),朝向为d0,(d=0代表向东,d=1 代表向南,d=2 代表向西,d=3 代表向北)如果机器人正对的方向可以走,那它就向前走一步,但是朝向不变,如果不可以走的话就向右转,即d'=(d+1)%4,但是位置保持不变。最后需要输出在机器人执行完 k 步操作之后,地图上所有被机器人经过的位置(包括起始位置)有几个。
2.这道题是模拟题,(可能有人会当成搜索),简单讲解下易错点,设置两个位移数组 dx[]={0,1,0,-1},dy[]={1,0,-1,0},从而简便地完成转向移动。(需要注意下坐标名称的命名,y1如果是全局变量的话,在万能头的情况下会编译错误),这种题坐标名称很容易弄混需要区分开。由于一组样例有多组数据,所以记得清空数组。还有需要注意的是,向右转也是一步,一个格子最多算一遍。
3.代码实现:
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
const int dx[]={0,1,0,-1},dy[]={1,0,-1,0};
bool vis[N][N];
char ch[N][N];
void solve(){
int n,m,k,x0,y0,d0;
memset(vis,0,sizeof(vis));
cin>>n>>m>>k;
cin>>x0>>y0>>d0;
for(int i=1;i<=n;i++){
char s[N];
cin>>s;
for(int j=1;j<=m;j++){
ch[i][j]=s[j-1];
}
}
vis[x0][y0]=true;
for(int i=1;i<=k;i++){
int x1=x0+dx[d0],y1=y0+dy[d0];
if(x1>=1&&x1<=n&&y1>=1&&y1<=m&&ch[x1][y1]=='.'){
x0=x1;
y0=y1;
}
else d0=(d0+1)%4;
vis[x0][y0]=true;
}
int ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
ans+=vis[i][j];
}
}
cout<<ans<<endl;
}
int main(){
int T;
cin>>T;
while(T--){
solve();
}
return 0;
}
小木棍
1.题目简述:给定一个自然数 n,使这 n 根木棍能摆出最小得数(不含前导 0)。
2.思路简析:先通过打n%7的余数的打表找一找特殊性质:
余数为 0:答案为n÷7个8。
余数为1:n=1特判,无答案输出-1。剩下的答案为10拼接(n-8)/7个8。
余数为2:答案为1拼接(n-2)/7个8。
余数为3:n=3特判,答案为7。n=10特判,答案为22。剩下的为200拼接(n-17)/7个8
余数为4:n=4特判,答案为4。剩下的为20拼接(n-11)/7个8。
余数为5:答案为2拼接(n-5)/7个8。
余数为6:答案为6拼接(n-6)/7个8。
3.代码实现:
#include<bits/stdc++.h>
using namespace std;
long long n,t;
int main(){
cin>>t;
while(t--){
cin>>n;
if(n==1) cout<<-1;
else if(n==2) cout<<1;
else if(n==3) cout<<7;
else if(n==4) cout<<4;
else if(n==5) cout<<2;
else if(n==6) cout<<6;
else if(n==7) cout<<8;
else if(n%7==0) for(int i=1;i<=n/7;i++) cout<<8;
else if(n%7==1){
cout<<10;
for(int i=1;i<=(n-8)/7;i++) cout<<8;
}
else if(n%7==2){
cout<<1;
for(int i=1;i<=(n-2)/7;i++) cout<<8;
}
else if(n%7==3){
if(n==10) cout<<22;
else{
cout<<200;
for(int i=1;i<=(n-17)/7;i++) cout<<8;
}
}
else if(n%7==4){
cout<<20;
for(int i=1;i<=(n-11)/7;i++) cout<<8;
}
else if(n%7==5){
cout<<2;
for(int i=1;i<=(n-5)/7;i++) cout<<8;
}
else if(n%7==6){
cout<<6;
for(int i=1;i<=(n-6)/7;i++) cout<<8;
}
cout<<"\n";
}
return 0;
}
接龙
(该说不说,T3T4题干都挺长的哈)
应该能想到是动态规划题。
先想一想暴力dp,为了避免多维的状态定义,我们把每个人的子序列化为长度最多为 2×10^5 的一维序列,并记录下每一张牌对应的人是谁。记这个一维序列的长度为 tot,显然。定义dp[i][j]表示当前进行到第 i 轮,且该轮以一维序列中的第 j 张牌结尾是否可行。状态转移:遍历第 j 张牌的前面 k−1 张牌(必须要是同一个人的),假设某张前面的牌所写的数字为 x,那么就可以遍历 r−1 层中不是同一个人的且值为 x 的人的牌转移过来,只要这些牌中有一个的结果为 1,那么第 j 张牌的值就是 1。
优化:注意到有一个限制是“不能从同一个人那里转移过来”,可以记录两个属于不同人的且可以转移过来的牌。只要这两张牌中,有一张和现在的牌所属的人不同,就是可以转移过来的。这个可以通过在每一轮结束之后预处理一遍得到。
进一步优化:观察到一个数的结果为 1,当且仅当前 k−1 位的预处理值中有一个可以转移。可能会想到单调队列优化进行转移。但这是普及组,不能考单调队列。于是我们换一种普及组思路。因为这题可以预处理,而查询操作是查询 l 到 r 之间有没有元素 1,因此预处理之后再前缀和一下就能做到 O(1) 查询有没有元素 1 了。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int T,n,k,q;
int l[N],xx[N],yy[N],dp[N][110];
vector<int>v[N];
int main(){
cin>>T;
while(T--){
cin>>n>>k>>q;
int maxl=0,maxr=0;
for(int i=1;i<=n;i++){
cin>>l[i];
v[i].clear();
for(int j=1;j<=l[i];j++){
int x;
cin>>x;
maxl=max(maxl,x);
v[i].push_back(x);
}
}
for(int i=1;i<=q;i++){
cin>>xx[i]>>yy[i];
maxr=max(maxr,xx[i]);
}
for(int i=0;i<=maxl;i++){
for(int j=0;j<=maxr;j++){
dp[i][j]=-1;
}
}
dp[1][0]=0;
for(int i=1;i<=maxr;i++){
for(int j=1;j<=n;j++){
int x=-1;
for(int k1=0;k1<v[j].size();k1++){
int t1=v[j][k1];
if(k1>=k){
int t2=v[j][k1-k];
if(dp[t2][i-1]!=-1&&dp[t2][i-1]!=j&&x==k1-k) x=-1;
}
if(x!=-1){
if(dp[t1][i]==-1)dp[t1][i]=j;
else if(dp[t1][i]!=j)dp[t1][i]=0;
}
if(dp[t1][i-1]!=-1&&dp[t1][i-1]!=j)x=k1;
}
}
}
for(int i=1;i<=q;i++){
if(yy[i]>maxl)cout<<"0";
else cout<<(dp[yy[i]][xx[i]]!=-1);
puts("");
}
}
return 0;
}