1. 程式人生 > 其它 >狀壓dp學習筆記(紫例題集)

狀壓dp學習筆記(紫例題集)

P3451旅遊景點 Tourist Attractions

這個程式碼其實不算是正規題解的(因為我蒟蒻)是在我們的hzoj上記憶體限制324MIB情況下過掉的,而且經過研究感覺不太能用滾動陣列,所以那這個題學習一下狀壓dp思想還是勉強可以的


  1 /*
  2 (可以不看) 
  3   (竊竊地)廢話:
  4    想了半天還是寫一篇題解吧,儘管有點麻煩。。。。 
  5    但這題的確做了不下十幾節課。。。。。
  6    不寫一篇對不起這幾天犧牲的公自了(慘) 
  7 */ 
  8 #include<bits/stdc++.h>
  9 using namespace
std; 10 const int NN=200005,MM=20005; 11 int n,m,q,k; 12 struct SNOW{ 13 int to,next,value; 14 }e[NN<<1]; 15 int r[NN<<1],tot=0; 16 int dis[25][MM],a[25]; 17 bool v[MM]; 18 int dp[1<<20][25]; 19 struct snow{ 20 int q,data; 21 friend bool operator<(snow a,snow b){return
a.data>b.data;} 22 }; 23 snow cc; priority_queue<snow> Q; 24 inline void add(int x,int y,int z){ 25 e[++tot]=(SNOW){y,r[x],z}; 26 r[x]=tot; 27 } 28 inline void WSN(int st){ 29 int x,y; 30 cc.q=st ;cc.data=0; 31 Q.push(cc); 32 memset(v,false,sizeof(v));//一定記得bool陣列清零,這就是你調了一個大會還沒過的錯誤。。。。。。。。。
33 dis[st][st]=0; 34 while(!Q.empty()){ 35 x=Q.top().q;y=Q.top().data; 36 Q.pop(); 37 if(!v[x]){ 38 v[x]=true; 39 for(int i=r[x];i;i=e[i].next){ 40 cc.q=e[i].to; 41 if(dis[st][cc.q]>y+e[i].value){ 42 dis[st][cc.q]=y+e[i].value; 43 cc.data=dis[st][cc.q]; 44 Q.push(cc); 45 } 46 } 47 } 48 } 49 } 50 inline void init(){ 51 dp[0][1]=0; 52 for(int i=2;i<=k+1;i++) 53 if(!a[i]) dp[1<<(i-2)][i]=dis[1][i]; 54 } 55 inline int read(){ 56 int x=0,f=1; char ch=getchar(); 57 while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } 58 while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar();} 59 return x*f; 60 } 61 void write(int x){ 62 if(x<0){ putchar('-'); x=-x;} 63 if(x>9) write(x/10); 64 putchar(x%10+'0'); 65 } 66 signed main(void) 67 { 68 memset(dis,50,sizeof(dis));//陣列都要開小一點,否則會爆炸(你調了兩天才發現的。。。。) 69 n=read(),m=read(),k=read(); 70 int mzs=1<<k; 71 for(int i=1;i<=m;i++) 72 { 73 int x=read(),y=read(),z=read(); 74 add(x,y,z); add(y,x,z); 75 } 76 if(k==0) { WSN(1); write(dis[1][n]); putchar('\n'); return 0;} 77 for(int i=1;i<=k+1;i++) WSN(i); 78 q=read(); 79 for(int i=1;i<=q;i++) 80 { 81 int f=read(),l=read(); 82 if(f>(k+1)||l>(k+1)) continue; 83 a[l]|=(1<<(f-2));//找到具有較高優先順序的位置並賦值為 1 84 } 85 memset(dp,50,sizeof(dp)); 86 init(); 87 for(int i=0;i<mzs;i++)//迴圈找1 88 for(int j=0;j<k;j++)//迴圈起點 89 for(int u=0;u<k;u++)//迴圈終點 90 { 91 if((i&(1<<j))==0) continue; 92 if((i&(1<<u))==0 && (i|a[u+2])==i/*如果有優先順序的點不包含在i情況下就跳過*/ ) 93 dp[i|(1<<u)][u+2]=min(dp[i|(1<<u)][u+2],dp[i][j+2]+dis[j+2][u+2]); 94 //dp[i][j]表示i狀態下經過的中間城市的編號 95 } 96 int wsn=0x3fffffff; 97 for(int i=2;i<=k+1;i++) 98 wsn=min(wsn,dp[(1<<k)-1][i]+dis[i][n]); 99 write(wsn); putchar('\n'); 100 return 0; 101 }//思想:分裂做法。 102 //將整個圖分裂成1~(2~k+1)和(2~k+1)~n,如下圖 103 /* 104 2 105 | 106 | 107 | 108 | 109 | 110 1 | n 111 | 112 | 113 | 114 | 115 k+1 116 dp的部分是前面的半張圖,而後面的半張圖用最後的迴圈搞定即可(第89,90行程式碼處) 117 其他的註釋很清楚了 118 */
View Code

P3622動物園

人生中第二篇題解,因為這題的確很喵~怎麼說呢,感覺不用賦值最小值(用的用的)。。。算了程式碼裡說的挺清楚,這裡就不叨叨那麼多了(思路在題解最後)

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std ;
 4 const int MM =10005;
 5 int N,C,snow,mzs=(1<<5);
 6 int dp[ MM ][(1<<5)+5],wsn[ MM ][(1<<5)+5];
 7 //表示起點為i的五個數字(及小朋友可以看到的動物)
 8 //在一定的狀態下可以使小朋友開心的個數(由此可見夜聊的好處~//^v^//~)
 9 // dp[i][j]=min(dp[i][j],dp[i][j]+wsn[i][j]); 
10 inline int read (){
11     int x=0,f=1; char ch=getchar();
12     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
13     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar();}
14     return x*f;
15 }
16 void write(int x){
17     if(x<0){ putchar('-'); x=-x;}
18     if(x>9) write(x/10);
19     putchar(x%10+'0');
20 }
21 signed main()
22 {
23     N=read(),C=read();
24     for(int i=1;i<=C;i++)
25     {
26         int E=read(),F=read(),L=read(),hate=0,like=0;
27         for(int j=1;j<=F;j++)hate|=1<<((read()-E+N)%N);
28         for(int j=1;j<=L;j++)like|=1<<((read()-E+N)%N);//這一步可以,往下找if判斷快樂條件 
29         for(int j=0;j<mzs;j++) if( (like&j) || (hate&~j) ) wsn[E][j]++;
30     }
31     for(int l=0;l<mzs;l++)//大的迴圈列舉不同的開始狀態,因為是環,要用每個為開始狀態進行找最值,因為最後會轉回來(即有重複)
32     {
33         for(int r=0;r<=32;r++) dp[0][r]=-999999999;
34         dp[0][l]=0;
35         for(int i=1;i<=N;i++)
36             for(int j=0;j<mzs;j++)
37             {
38                 int k=((j&~(1<<4))<<1);//K是把原來的五位二進位制數首位取零,並向左移動一位(其實與(j&15)<<1一樣) 
39                 int u=(k|1);// U是列舉移動一位後最右一位的另一種情況(分別為0,1。0的情況既是k,u是1) 
40                 dp[i][j]=max(dp[i-1][k],dp[i-1][u])+wsn[i][j];
41             }
42         snow=max(snow,dp[N][l]);//在情況內尋找最大值
43     }
44     write(snow); putchar('\n');
45     return 0;
46 }/*思路一:需要預處理:
47     將三種情感看作兩兩匹配的,及喜歡和中性為一組,討厭和中性為一組。
48         進行預處理時分開處理,可以解決三進位制無法實現的問題。
49     思路二:dp如何做:
50         在迴圈的時候每次將五位二進位制數首位改成零,然後再向左移動一位,
51         表示狀態向左轉移了一位(這樣不用處理環)然後再將新增加的一位
52         分別用0,1表示不同的情況,比較出最大值,在加上當前狀態可以有
53         的小朋友數(程式碼中的解釋也十分詳細)。
54 */
View Code