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];//找到根节点的pos
if(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
{
//正常情况下应该相差1
if(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;
}