The 2019 ICPC Asia Shanghai Regional Contest

K.Color Graph

题意:给出一个由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 }
View Code

 

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 }
View Code

 D. Spanning Tree  Removal

思路:构造题,可以知道最大个数就是n/2;

           构造方式为:按照z字形的方式构造

按编号从小到大逆时针排列在一个圆上。

  1. 以点 x 为起点,顺时针走 1 个点到点 y , x 和 y 之间删边。
  2. 以点 y 为起点,逆时针走 2 个点到点 z , y 和 z 之间删边。
  3. 以点 z 为起点,顺时针走 3 个点到点 k , z 和 k 之间删边。

  4.  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 }

     

posted @ 2020-09-04 12:44  古比  阅读(310)  评论(0编辑  收藏  举报