1. 程式人生 > >Gym - 100920E 2010-2011 OpenCup IX Onsite, II Yandex Summer School E. Paint 暴力+狀壓DP

Gym - 100920E 2010-2011 OpenCup IX Onsite, II Yandex Summer School E. Paint 暴力+狀壓DP

gym 最小 pen 存在 == space 重新 attach open

題面

題意:給你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 }

Gym - 100920E 2010-2011 OpenCup IX Onsite, II Yandex Summer School E. Paint 暴力+狀壓DP