Gym - 100920E 2010-2011 OpenCup IX Onsite, II Yandex Summer School E.Paint 状压DP

题面

题意:给你n(20)个点,m(40条边),让你给每条边染一种颜色,白色0元,红色2元,蓝色1元,现在要保证每一条白边相邻的有一条红边,问至少花多少

题解:刚开始想的时候,好像觉得只用染红色和白色就够了?乱搞一通就发现不对了

        例如:5个点,5条边:1 2,   2 3  ,  3 4 , 4 5 , 5 2 .  如果只用红白,则答案为2条红边,2*2=4  但实际上,如果2 5染红,3 4染蓝,答案为3

        所以我们重新思考:什么时候染蓝色?

        我们假设一条边,如果染了红色,则他的2个端点,就标记为红色,如果我们枚举每个点是否红色(2^20),然后再看这个图

        我们发现,此时如果存在一条边的两个端点都没被染红,那么把这条边染蓝更优.

        所以我们利用状压,枚举每个点染红的状态(注意这个状态其实也是由边统计来的)

        dp[i]表示i集合为红的最小花费    dp[i+t]=dp[i]+2;  t=i | (1<<u) | (1<<v)  (其实u,v都要减一,不过在读入的时候处理掉了)

        然后再枚举每个状态,看蓝色的花费,统计出最小的答案

       

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,m,a[45],b[45],f[1024*1024+10];
 4 int main()
 5 {
 6     scanf("%d%d",&n,&m);
 7     for (int i=1;i<=m;i++) scanf("%d%d",&a[i],&b[i]),a[i]--,b[i]--;
 8     int nn=1<<n;
 9     for (int i=0;i<nn;i++) f[i]=(int)1e8;
10     f[0]=0;
11     for (int i=0;i<nn;i++)
12     {
13         if (f[i]!=(int)(1e8))
14         {
15             for (int j=1;j<=m;j++)
16             {
17                 int t=i|(1<<a[j])|(1<<b[j]);
18                 f[t]=min(f[t],f[i]+2);
19             }
20         }
21     }
22     int ans=(int)(1e8);
23     for (int i=0;i<nn;i++)
24     {
25         int qq=f[i];
26         for (int j=1;j<=m;j++)
27         {
28             if ((i>>a[j])%2==0 && (i>>b[j])%2==0) qq++; 
29         }
30         ans=min(ans,qq);
31     }
32     cout<<ans<<endl;    
33 }

 

                          

posted @ 2019-03-31 21:40  口香糖万岁  阅读(163)  评论(0编辑  收藏  举报