1. 程式人生 > >[UVA11825]Hackers' Crackdown(狀壓dp)

[UVA11825]Hackers' Crackdown(狀壓dp)

題解降智警告

吐槽降智警告

思路降智警告

程式碼降智警告

題目傳送門 洛谷

果然水題做多了連半道難點的都能給咱幹蒙...

水題做多了降智  --魯迅


 

題目大意:見傳送門

心路歷程見末尾

正解(大概):

狀壓

雖然有些難以理解,但是這道題裡面有兩種集合

一種是第i臺電腦所聯通的合起來就是S[i](程式碼內的line[i])

另一種是指已經用了哪幾臺電腦

 

思路開始

反正無論如何一定得用幾個S[i]拼成一個全集(1<<n)-1

於是就有了

 1 struct Union
2 { 3 int A; 4 }U[1<<16];//可以湊出來一個全集的幾個電腦的集合 5 int Uarr; 6 void getU(int st,int A,int p) 7 { 8 if(st==((1<<n)-1))//這幾個電腦的line集合拼成了全集 9 { 10 U[Uarr].A=A; 11 Uarr++; 12 return; 13 } 14 if(p>=n) return; 15 getU(st|line[p],A|(1
<<p),p+1);//集合里加這個電腦 16 getU(st,A,p+1);//不加 17 }
好孩子不要急著就看哦(大霧)

(寫法比較樸素的說)

好了我們的重點部分已經完事了

(啥你這就完事了?)

確實想到這個就好寫了...

如果你做過[NOIP2016]憤怒的小鳥

那這題就更好想到了

好了上程式碼

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using std::sort;
 5 int max(int
a,int b){return a>b?a:b;} 6 int n,m; 7 8 int dp[1<<16]; 9 int line[16]; 10 11 struct sta 12 { 13 int s,sc; 14 }s[1<<16]; 15 int sarr; 16 void getsta(int st,int sc,int p) 17 { 18 if(p>=n) 19 { 20 s[sarr].s=st; 21 s[sarr].sc=sc; 22 sarr++; 23 return; 24 } 25 getsta(st|(1<<p),sc+1,p+1); 26 getsta(st,sc,p+1); 27 } 28 29 struct Union 30 { 31 int A; 32 }U[1<<16]; 33 int Uarr; 34 void getU(int st,int A,int p) 35 { 36 if(st==((1<<n)-1)) 37 { 38 U[Uarr].A=A; 39 Uarr++; 40 return; 41 } 42 if(p>=n) return; 43 getU(st|line[p],A|(1<<p),p+1); 44 getU(st,A,p+1); 45 } 46 47 bool cmp(sta a,sta b)//狀態按照1的數量排序(咱就好這一手) 48 { 49 if(a.sc==b.sc) return a.s<b.s; 50 else return a.sc<b.sc; 51 } 52 53 void memclr() 54 { 55 memset(line,0,sizeof(line)); 56 memset(dp,0,sizeof(dp)); 57 Uarr=0; 58 sarr=0; 59 } 60 int main() 61 { 62 int xin,kk=0; 63 while(scanf("%d",&n)!=EOF) 64 { 65 if(n==0) return 0; 66 memclr(); 67 getsta(0,0,0); 68 sort(s,s+sarr,cmp); 69 for(int i=0;i<n;i++) 70 { 71 scanf("%d",&m); 72 line[i]|=(1<<i); 73 for(int j=1;j<=m;j++) 74 { 75 scanf("%d",&xin); 76 line[i]|=(1<<xin); 77 } 78 } 79 getU(0,0,0); 80 for(int si=0;si<sarr;si++) 81 { 82 for(int Ui=0;Ui<Uarr;Ui++) 83 { 84 if((s[si].s&U[Ui].A)==U[Ui].A) 85 { 86 dp[s[si].s]=max(dp[s[si].s],dp[s[si].s^U[Ui].A]+1); 87 } 88 } 89 } 90 printf("Case %d: %d\n",++kk,dp[(1<<n)-1]); 91 } 92 return 0; 93 }
好孩子要自己思考~

看上去做的十分輕鬆

實際上

心路歷程如下:

一開始:emmmm外層電腦使用狀態裡面列舉下一個使用每個電腦然後dp裡存v和狀態...

寫完:???

等下等下從下面能繼承過來一堆狀態啊啊啊

變成(2^n)^k了啊啊啊啊啊

心態爆炸

Ah~(油庫裡音)

 

切題後:

啥?子集的子集是3^n?

完了我菜死了

陷入沉思...

3^n*n似乎也會T死

這至少證明了我原先的思路似乎沒啥可行性而不是半途而廢(什麼邏輯)

心態平衡了

Ah~(油庫裡音)


 

以上

本題完結

記住

水題做多了降智

 

我沒說過這句話 --魯迅