A. Bear and Friendship Condition(思维or完全图判定)
•题意
给你n个人,m个朋友关系
朋友是会传递的,若A B是朋友,A C是朋友,则必须有B C的朋友关系
符合这个关系输出YES,否则输出NO
•思路
n个人,但凡是有朋友关系的,必定在同一个朋友圈内
所以可以分成若干个朋友圈
在一个朋友圈内部,若符合条件肯定是互为朋友
也就是 是一个完全图
接下来就是判断是否是完全图了
举个栗子:1234在一个朋友圈内,且符合条件
则人与朋友的对应关系为
1与1 2 3 4为朋友
2与1 2 3 4为朋友
3与1 2 3 4为朋友
4与1 2 3 4为朋友
即,他们的朋友是完全相同的!
挨个去判断朋友是否相同显然时间复杂度不够优雅
只需要排序后,看第一个朋友是否相等就可以
O(n)变O(1)!
为什么呢? 因为每个人的朋友圈都要去查看一遍,如果A的朋友圈没有B,但是B的朋友圈有A
那么,B和A的朋友圈肯定对不上,所以就输出NO了
•代码
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 const int maxn=2e5+5; 5 vector<int> a[maxn]; 6 //我加vis数组是为了减少不必要的查看来减少时间 7 //氮素 貌似效率还不如不加vis的,搞不懂 ??? 8 bool vis[maxn]; 9 10 int main() 11 { 12 // freopen("C:\Users\14685\Desktop\C++workspace\in&out\contest","r",stdin); 13 int n,m; 14 cin>>n>>m; 15 for(int i=1;i<=m;i++) 16 { 17 int x,y; 18 cin>>x>>y; 19 a[x].push_back(y); 20 a[y].push_back(x); 21 } 22 ///把自己也加进自己的朋友圈 23 for(int i=1;i<=n;i++) 24 a[i].push_back(i); 25 ///排序 26 for(int i=1;i<=n;i++) 27 sort(a[i].begin(),a[i].end()); 28 29 ///查看每个人的朋友圈 30 for(int i=1;i<=n;i++) 31 { 32 if(vis[i]) 33 continue; 34 ///自己的朋友圈和朋友的朋友圈对比 35 for(int j=0;j<a[i].size();j++) 36 { 37 if(a[i].size()==a[a[i][j]].size()) 38 { 39 vis[a[i][j]]=true; 40 ///朋友圈不相同 41 if(a[i][0]!=a[a[i][j]][0]) 42 { 43 puts("NO"); 44 return 0; 45 } 46 } 47 else 48 { 49 puts("NO"); 50 return 0; 51 } 52 } 53 } 54 puts("YES"); 55 }
B - Bear and Different Names(模拟)
•题意
有n个人,从1个人开始每k个一组
[1,k] [2,k+1]....
如果有重名的则NO,否则YES
根据NO和YES的结果输出名字
名字首字母大写,且1<=名字长度<=10
•思路
因为名字长度有限制,可能有很多人名字不同
所以首先预处理出不同名字来,注意计算名字个数
(这里踩了坑)
首先找到第一个YES为切入点,后边的名字可以由他得到
已经确定了的名字不能再更改!否则会引起与前面的YES/NO不符 (这里也踩了坑
如果是YES的话就选择新名字,否则选择和这k个人的第一个人相同的名字(也就是首尾名字重合
然后再找第一个YES前面的人,从第一个YES开始倒找,让他前面的都和他重名
•代码
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 const int maxn=2e5+5; 5 string name[55],nname[3000]; 6 string s[55]; 7 int wh[10]; 8 ///预处理名字 9 void Init() 10 { 11 for(int i=0;i < 10;++i) 12 wh[i]=i; 13 int cnt=0; 14 do 15 { 16 for(int i=0;i<10;i++) 17 { 18 if(i==0) 19 nname[++cnt]=wh[0]+'A'; 20 else 21 nname[cnt]+=(wh[i]+'a'); 22 } 23 if(cnt>2500) 24 return ; 25 }while(next_permutation(wh,wh+10)); 26 } 27 int main() 28 { 29 // freopen("C:\Users\14685\Desktop\C++workspace\in&out\contest","r",stdin); 30 int n,m; 31 cin>>n>>m; 32 bool flag=false; 33 Init(); 34 int cut=0; 35 for(int i=1;i<=n-m+1;i++) 36 { 37 cin>>s[i]; 38 if(s[i]=="YES") 39 { 40 flag=true; 41 for(int j=i;j<=i+m-1;j++) 42 if(name[j].empty()) 43 name[j]=nname[++cut];///选择新名字 44 } 45 else///这k个人首尾名字重合 46 name[i+m-1]=name[i]; 47 } 48 49 if(!flag) 50 for(int i=1;i<=n;i++) 51 cout<<"Aa"<<' '; 52 else 53 { 54 ///找第一个YES 55 int index; 56 for(index=1;index<=n-m+1;index++) 57 if(s[index]=="YES") 58 break; 59 ///前面的人和他重名 60 for(int i=index;i>=0;i--) 61 name[i]=name[index]; 62 63 for(int i=1;i<=n;i++) 64 cout<<name[i]<<' '; 65 } 66 }
D . Bear and Company(带技巧的dp)
•题意
有一种操作可交换相邻两个字母
现给出一个S串,要求没有VK相连,求最小的操作数
•思路
在ljp的帮助下(tql...),终于AC了这个题
s串中包含三种字母,V K和其他字母(因为其他字母无论是什么对VK都不产生影响)
设dp数组$f[i][j][k][0/1]$
前$i$个V,$j$个K,$k$个其他字母结尾不是$V$/不是$V$的最小操作数
把V K和Other 分开
for(int i=0;i<n;i++) {
if(s[i]=='V') v[0].push_back(i); else if(s[i]=='K') v[1].push_back(i); else v[2].push_back(i); }首先看作一个空串,然后往后面添加字母
V可以接在V K Other后面
K可以接在K Other后面
Other可以接在V K Other后面
由于是接在后面的,那就要把本来应该在他后面但是现在在他前面的字母转移到后面
并且这些字母与他只会进行一次交换
例如ABCD中A转移到D后面,
ABCD->BACD->BCAD->BCDA
A只会和D进行一次交换
添加V使得V在末尾影响$f[i][j][k][1]$
添加K使得V不在末尾影响$f[i][j][k][0]$
添加Other使得V不在末尾影响$f[i][j][k][0]$
由于添加K和Other都是V不在末尾,如果两者都添加的话,一定要取最小值
f数组必须初始化,因为有依赖关系
•代码
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 ///v[0]:V v[1]:K v[2]:O 4 vector<int> v[3]; 5 ///f[i][j][k][0/1] 6 ///前i个V,j个K,k个O 尾位是否是V 的最小交换 7 int f[80][80][80][2]; 8 int n; 9 string s; 10 int main() 11 { 12 cin>>n>>s; 13 for(int i=0;i<n;i++) 14 { 15 if(s[i]=='V') 16 v[0].push_back(i); 17 else if(s[i]=='K') 18 v[1].push_back(i); 19 else 20 v[2].push_back(i); 21 } 22 23 ///初始化 24 for(int i=0;i<=v[0].size();i++) 25 for(int j=0;j<=v[1].size();j++) 26 for(int k=0;k<=v[2].size();k++) 27 f[i][j][k][0]=f[i][j][k][1]=0x3f3f3f3f; 28 f[0][0][0][0]=0; 29 30 int cnt=0; 31 for(int i=0;i<=v[0].size();i++) 32 { 33 for(int j=0;j<=v[1].size();j++) 34 { 35 for(int k=0;k<=v[2].size();k++) 36 { 37 if(i||j||k) 38 { 39 if(i)///0 V 40 { 41 ///V可以接在V K O后面 42 cnt=min(f[i-1][j][k][0],f[i-1][j][k][1]); 43 ///交换位置 K 1 44 for(int m=0;m<j;m++) 45 if(v[1][m]>v[0][i-1]) cnt++; 46 ///交换位置 O 2 47 for(int m=0;m<k;m++) 48 if(v[2][m]>v[0][i-1]) cnt++; 49 50 f[i][j][k][1]=cnt; 51 } 52 if(j)///1 K 53 { 54 ///K可以接在K O后面 55 cnt=f[i][j-1][k][0]; 56 ///交换位置 V 0 57 for(int m=0;m<i;m++) 58 if(v[0][m]>v[1][j-1]) cnt++; 59 ///交换位置 O 2 60 for(int m=0;m<k;m++) 61 if(v[2][m]>v[1][j-1]) cnt++; 62 63 f[i][j][k][0]=cnt; 64 } 65 if(k)///2 O 66 { 67 ///O可以接在V K O后面 68 cnt=min(f[i][j][k-1][0],f[i][j][k-1][1]); 69 ///交换位置 V 0 70 for(int m=0;m<i;m++) 71 if(v[0][m]>v[2][k-1]) cnt++; 72 ///交换位置 K 1 73 for(int m=0;m<j;m++) 74 if(v[1][m]>v[2][k-1]) cnt++; 75 76 f[i][j][k][0]=min(f[i][j][k][0],cnt);///取最小值 77 } 78 } 79 } 80 } 81 } 82 83 int i=v[0].size(),j=v[1].size(),k=v[2].size(); 84 cout<<min(f[i][j][k][0],f[i][j][k][1])<<endl; 85 }