状压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 */ 
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

 

 

以上是关于状压dp学习笔记(紫例题集)的主要内容,如果未能解决你的问题,请参考以下文章

ybtoj 状压DP课堂过关AcWing 91最短 Hamilton 路径 &例题2最短路径

动态规划---状压dp

BZOJ 2595: [Wc2008]游览计划 [DP 状压 斯坦纳树 spfa]学习笔记

ybtoj 状压DP课堂过关 例题1jzoj 1266 luogu P1879 [USACO06NOV]Corn Fields G & 玉米田 & 种植方案

旅行商问题状压DP

HDU4057 Rescue the Rabbit(AC自动机+状压DP)