3777. 砖块(每日一题)
后面if条件判断很巧妙,只要有一种成立就不管另一种了。
#include<bits/stdc++.h>
using namespace std;
//3777. 砖块
//跟贪心差不多
string s;
int t,n;void update(char &c)
{if(c=='W')c='B';else c='W';
}bool check(char c)
{vector<int>a;string ss=s;for(int i=0;i+1<n;i++){if(ss[i]!=c){update(ss[i]);update(ss[i+1]);a.push_back(i);}}if(ss.back()!=c)return false;cout<<a.size()<<endl;for(int i:a)cout<<i+1<<" ";if(a.size()>0)cout<<endl;return true;}
int main()
{cin>>t;for(int i=0;i<t;i++){cin>>n;cin>>s;if(!check('B')&&!check('W'))cout<<"-1"<<endl;}
}
1208. 翻硬币
和翻砖头一样,(赋值用成了==浪费了10min)
#include<bits/stdc++.h>
using namespace std;
//1208. 翻硬币
string s,t;
void update(int i)
{if(s[i]=='*')s[i]='o';else s[i]='*';
}
int main()
{cin>>s;cin>>t;int cnt=0;for(int i=0;i<s.size();i++){if(s[i]!=t[i]){update(i);update(i+1);cnt++;}}cout<<cnt<<endl;}
递归 1497 树的遍历
自己的做法如下(是错误的):没做出来:
#include<bits/stdc++.h>
using namespace std;
const int N=30;
//1497. 树的遍历(错误的)
vector<int>a(N);
vector<int>b(N);
vector<int>d(N);
unordered_map<int,int>mp;//某个点是在哪个位置
int n;void countt(int c,int cnt )
{if(cnt>n)return;int t=a[c];int pos=mp[t];d[cnt]=t;cnt*=2;countt(pos-1,cnt);countt(c-1,cnt+1);
}
int main()
{cin>>n;for(int i=1;i<=n;i++){cin>>a[i];}//for(int i=1;i<=n;i++)cout<<a[i]<<endl;for(int i=1;i<=n;i++){cin>>b[i];mp[b[i]]=i;}countt(n,1);int num=1,lay=0;while(num<=n){for(int j=num;j<num+1<<lay;j++){cout<<d[j]<<" ";}lay++;num=2<<lay;}
}
y做法,递归的参数有四个。每层递归的作用就是找到后序遍历的最后一个结点,也就是根节点。找出其在中序遍历中的位置。相当于进行了一个划分。然后递归求左右子树的根节点,保存。最后返回根节点。递归的条件是:划分之后左子树的中序遍历左端点是否在pos左边,右端点是否在pos右边。
#include<bits/stdc++.h>
using namespace std;
const int N=30;
//1497. 树的遍历
int a[N],b[N];
unordered_map<int,int>l,r,p;//用来保存某个点的左右子树
int n;//每次返回左子树或者右子树
int build(int ml,int mr,int pl,int pr)
{int t=a[pr];//找到根节点的值int pos=p[t];//找到根节点的posif(ml<pos)l[t]=build(ml,pos-1,pl,pl+pos-1-ml);if(mr>pos)r[t]=build(pos+1,mr, pl+pos-ml,pr-1);return t;
}void bfs(int t)
{queue<int>q;q.push(t);while(!q.empty()){int u=q.front();cout<<u<<" ";q.pop();if(l.count(u))q.push(l[u]);if(r.count(u))q.push(r[u]);}}int main()
{int n;cin>>n;for(int i=0;i<n;i++){cin>>a[i];}for(int i=0;i<n;i++){cin>>b[i];p[b[i]]=i;//保存中序位置}int t=build(0,n-1,0,n-1);bfs(t);}
//1249. 亲戚
找亲戚问题:我犯的错是:让f【l】=find(r)。我只想到了两个人有亲戚就这样赋值。
其实是错误的,两个人有亲戚是代表着两个大集合之间实现了连通,所以要找到l的祖先赋值为r的祖先,这样就能代表整个集合。否则只有l能和r有亲戚。
#include<bits/stdc++.h>
using namespace std;
const int N=20000;
//1249 亲戚
int f[N];
int findf(int x)
{if(f[x]==x)return x;else return findf(f[x]);
}
int main()
{int n,m;cin>>n>>m;for(int i=1;i<=n;i++)f[i]=i;for(int i=0;i<m;i++){int l,r;cin>>l>>r;f[findf(l)]=findf(r);}int q;cin>>q;for(int i=0;i<q;i++){int l,r;cin>>l>>r;if(findf(l)==findf(r))cout<<"Yes"<<endl;else cout<<"No"<<endl;}}
//836合并集合
此题思路没有问题了,但是在输入上有问题。
复习了getcahr();
当我们希望读取一行字符,包括其中的空格的时候一定要加上getchar(),因为前面遗留了一个换行符号。
cin>>n>>m;//getchar();//这个时候如果没有getchar,s就会读取换行符号getline(cin,s);cout<<s;
getline之间不用加(cin会把换行符留在缓冲区里面,等待被其他变量获取,而getline会读取换行符,并丢弃掉)
cin>>n>>m;getchar();//这个时候如果没有getchar,s就会读取换行符号getline(cin,s);getline(cin,s);cout<<s;
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
//836 合并集合
//(重点是怎么处理字符串)
char s[2];
int n,m;
int f[N];int getf(int x)
{if(f[x]==x)return x;else return getf(f[x]);
}int main()
{cin>>n>>m;for(int i=1;i<=n;i++)f[i]=i;while(m--){int l,r;scanf("%s%d%d", s, &l, &r);if(s[0]=='M'){f[getf(l)]=getf(r);}else{if(getf(l)==getf(r))puts("Yes");else puts("No");}}}
// 837 连通块中点的数量
做出来了,但是TLE
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
//837 连通块中点的数量 TLE
int n,m;
char s[2];
int l,r;
int f[N];int getf(int x)
{if(f[x]==x)return x;else return getf(f[x]);
}int main()
{cin>>n>>m;for(int i=1;i<=n;i++)f[i]=i;while(m--){cin>>s;if(s[0]=='C'){cin>>l>>r;f[getf(l)]=getf(r);}else if(s[0]=='Q'&&s[1]=='1'){cin>>l>>r;if(getf(l)==getf(r)){cout<<"Yes"<<endl;}else cout<<"No"<<endl;}else{int cnt=0;cin>>l;for(int i=1;i<=n;i++){if(getf(i)==getf(l)){cnt++;}}cout<<cnt<<endl;}}
}
y做法:自己的做法的错误:在计算一个连通子块的时候可以用数组存。因为每个子块一定有一个唯一的共同的祖先。用这个点存cnt值。
并查集太博大精深了,以前觉得很好理解,其实里面还有很多细节,在用cnt存的时候,只有两个结点不在同一个集合里面才能,加cnt。否则会重复。
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
//837 连通块中点的数量
int n,m;
char s[2];
int l,r;
int f[N];
int cnt[N];
int getf(int x)
{if(f[x]==x)return x;else return getf(f[x]);
}int main()
{cin>>n>>m;for(int i=1;i<=n;i++)f[i]=i,cnt[i]=1;while(m--){cin>>s;if(s[0]=='C'){cin>>l>>r;if(getf(r)!=getf(l)){f[getf(l)]=getf(r);cnt[getf(r)]+=cnt[getf(l)];}}else if(s[0]=='Q'&&s[1]=='1'){cin>>l>>r;if(getf(l)==getf(r)){cout<<"Yes"<<endl;}else cout<<"No"<<endl;}else{cin>>l;cout<<cnt[getf(l)]<<endl;}}}
240 食物链(带权并查集)
思考时的难点是三个种群的区别。
这道题不是一个种群一个集合了,是一个完整的食物网一个集合。
食物网的每条路一定是按照题目给出的食物三角形走的。所以对于同一个食物网上的两个物种,判断其关系。就要有一个起点,也就是这个食物网的祖先结点。计算到根节点的距离。得到相对距离取模。
对于没有在同一个网上的两个同类物种。一定是对的。(只要我们更新好d)。让x的祖先结点指向y的祖先结点。且更新d为dy-dx。这样就保证捕食关系正确了。
#include<bits/stdc++.h>
using namespace std;
const int N=50010;
//240食物链
int f[N];
int d[N];
int getf(int x)
{if(f[x]==x)return x;else{int t=getf(f[x]);d[x]+=d[f[x]];//计算到根节点的距离//先保存d,再更新f[x]f[x]=t;}return f[x];
}int main()
{int n,m,cnt=0;cin>>n>>m;for(int i=1;i<=n;i++)f[i]=i;int res=0;while(m--){int t,x,y;cin>>t>>x>>y;if(x>n||y>n)cnt++;else{int px=getf(x);int py=getf(y);if(t==1){if(px==py&&(d[x]-d[y])%3)cnt++;else if(px!=py){f[px]=py;d[px]=(d[y]-d[x]);}}else{//正常情况下应该相差1if(px==py&&(d[x]-d[y]-1)%3)cnt++;else if(px!=py){f[px]=py;d[px]=d[y]-d[x]+1;}}}}cout<<cnt;return 0;
}