The 2019 ICPC Asia Shanghai Regional Contest
题意:给出一个由n个点和m 条边组成的无向图,保证无自环无重边,初始时所有的边都是白色的,每一次都可以选择一条边把它染成红色,不过需要保证不存在红色的奇环,现在要求尽可能多的将白边染成红色,问最多能染多少条边
题解:
- 看到奇环首先想到二分图:所以这道题就转化成了一个二分图问题:就是要找尽可能多的边使得这些边组成的图是二分图。
- 看到数据范围首先想到状压(二进制):用一个二进制串表示每个点的状态(即是在二分图左半部分还是在右半部分呢)
第ii位二进制位为11,说明编号为ii的节点在二分图左半部分。为0则是在右半部分。对于二进制为11的点,我们把它们染色为1,对于二进制位为0的点,我们染色为0,然后遍历mm条边,如果边的两个端点颜色不同,那么符合条件的边的个数加11,如果端点颜色一样,即两个点同在左半部或者右半部, 则不能选择这条边
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 #define endl '\n' 5 const int maxn = 17*17; 6 int a[maxn],b[maxn]; 7 bool vis[17]; 8 9 int main() 10 { 11 int t,cas=0;cin>>t; 12 while( t-- ){ 13 int n,m; cin>>n>>m; 14 for(int i=1;i<=m;i++) cin>>a[i]>>b[i]; 15 int ans=0; 16 for(int j=0;j<1<<n;j++){ //枚举状态 17 for(int k=0;k<16;k++){ //遍历状态的每一位 18 vis[k]=(j>>k)&1; //如果第k位为1,染色为编号为i的节点1; 否则染色为0 19 } 20 int num=0; 21 for(int i=1;i<=m;i++){ 22 num+=vis[a[i]]^vis[b[i]];//第i条边的两段的点颜色一样异或为0代表不取,不一样以后为1则取 23 } 24 ans=max(ans,num); 25 } 26 printf("Case #%d: %d\n",++cas,ans); 27 } 28 return 0; 29 }
B.Prefix Code
题意:给出n个字符串(长度不超过10),让我们判断是否存在某一个字符串是其他字符串的前缀
思路:直接暴力判断
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e4+10; 4 map<string,int>mp; 5 string tmp[maxn]; 6 int main() 7 { 8 int T; 9 scanf("%d",&T); 10 int Case=0; 11 while(T--){ 12 mp.clear(); 13 int n; 14 scanf("%d",&n); 15 for(int i=1;i<=n;i++){ 16 cin>>tmp[i]; 17 mp[tmp[i]]++; 18 } 19 int flag=0; 20 for(int i=1;i<=n;i++){ 21 if(flag==-1) break; 22 int len=tmp[i].size(); 23 for(int j=1;j<len;j++){ 24 string t=tmp[i].substr(0,j); 25 if(mp[t]) flag=-1; 26 } 27 if(mp[tmp[i]]>=2) flag=-1; 28 } 29 printf("Case #%d: ",++Case); 30 if(flag==-1) printf("No\n"); 31 else printf("Yes\n"); 32 } 33 return 0; 34 }
E . Cave Escape
思路:题目给出了矩阵每一个位置的val的计算方法,以及相邻两个点之间的权值计算方法
我们考虑对相邻点建边,然后跑最大生成树,跑满n个点,即n-1条边即可
1 #include<bits/stdc++.h> 2 #pragma GCC optimize(2) 3 using namespace std; 4 typedef long long ll; 5 const int N=1e6+100; 6 7 int n,m,sr,sc,tr,tc; 8 int a,b,c,p,X[N]; 9 10 struct Edge{ 11 Edge(int x=0,int y=0,int val=0):x(x),y(y),val(val){} 12 bool operator<(const Edge &t){return val>t.val;} 13 int x,y,val; 14 }; 15 int fa[N],ran[N]; 16 int get(int x){return x==fa[x]?x:fa[x]=get(fa[x]);} 17 bool unite(int x,int y){ 18 x=get(x),y=get(y); 19 if(x==y) return false; 20 if(ran[x]<ran[y]) 21 fa[x]=y; 22 else{ 23 fa[y]=x; 24 if(ran[x]==ran[y]) ran[x]++; 25 } 26 return true; 27 } 28 inline int val(int i,int j){return X[(i-1)*m+j];} 29 Edge e[N*2]; 30 int tot=0; 31 int main(){ 32 int t,kase=0; 33 scanf("%d",&t); 34 while(t--){ 35 tot=0; 36 scanf("%d%d%d%d%d%d",&n,&m,&sr,&sc,&tr,&tc); 37 scanf("%d%d%d%d%d%d",X+1,X+2,&a,&b,&c,&p); 38 for(int i=1;i<=n*m;++i){fa[i]=i;ran[i]=0;} 39 for(int i=3;i<=n*m;++i)X[i]=(a*X[i-1]+b*X[i-2]+c)%p; 40 41 for(int i=1;i<=n;++i) 42 for(int j=1;j<=m;++j){ 43 if(i+1<=n)e[tot++]=Edge((i-1)*m+j,i*m+j,val(i,j)*val(i+1,j)); 44 if(j+1<=m)e[tot++]=Edge((i-1)*m+j,(i-1)*m+j+1,val(i,j)*val(i,j+1)); 45 } 46 sort(e,e+tot); 47 ll ans=0,cnt=n*m-1; 48 for(int i=0;i<tot;++i){ 49 Edge &t=e[i]; 50 //int u=get(t.x),v=get(t.y); 51 if(!unite(t.x,t.y)) 52 continue; 53 ans+=t.val; 54 if(--cnt==0)break; 55 } 56 printf("Case #%d: %lld\n",++kase,ans); 57 } 58 return 0; 59 }
D. Spanning Tree Removal
思路:构造题,可以知道最大个数就是n/2;
构造方式为:按照z字形的方式构造
按编号从小到大逆时针排列在一个圆上。
- 以点 x 为起点,顺时针走 1 个点到点 y , x 和 y 之间删边。
- 以点 y 为起点,逆时针走 2 个点到点 z , y 和 z 之间删边。
- 以点 z 为起点,顺时针走 3 个点到点 k , z 和 k 之间删边。
- …
1 #include <bits/stdc++.h> 2 using namespace std; 3 int main() 4 { 5 int t,n,cas=0; 6 scanf("%d",&t); 7 while(t--){ 8 scanf("%d",&n); 9 printf("Case #%d: %d\n",++cas,n/2); 10 for(int i=1;i<=n/2;i++){ 11 int L=i; 12 int R=(L+n-1)%n; 13 if(R==0) R=n; 14 R++; 15 int x=i; 16 int flag=0; 17 for(int j=1;j<n;j++){ 18 if(!flag){ 19 L++; 20 int y=L; 21 printf("%d %d\n",x,y); 22 x=y; 23 flag=1; 24 } 25 else{ 26 R--; 27 if(R==0) R=n; 28 int y=R; 29 printf("%d %d\n",x,y); 30 x=y; 31 flag=0; 32 } 33 } 34 } 35 } 36 return 0; 37 }