状压dp学习笔记(紫例题集)
Posted 雪域亡魂
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了状压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 */
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 */
以上是关于状压dp学习笔记(紫例题集)的主要内容,如果未能解决你的问题,请参考以下文章
ybtoj 状压DP课堂过关AcWing 91最短 Hamilton 路径 &例题2最短路径
BZOJ 2595: [Wc2008]游览计划 [DP 状压 斯坦纳树 spfa]学习笔记
ybtoj 状压DP课堂过关 例题1jzoj 1266 luogu P1879 [USACO06NOV]Corn Fields G & 玉米田 & 种植方案