状态压缩的DP之个人整理总结.docx
- 文档编号:8168224
- 上传时间:2023-01-29
- 格式:DOCX
- 页数:65
- 大小:128.77KB
状态压缩的DP之个人整理总结.docx
《状态压缩的DP之个人整理总结.docx》由会员分享,可在线阅读,更多相关《状态压缩的DP之个人整理总结.docx(65页珍藏版)》请在冰豆网上搜索。
状态压缩的DP之个人整理总结
状态压缩的DP
方格选数最大hdu21671
给奶牛安排牛场(pku2241)4
积木块(哈尔滨5085bircks)7
哈密顿回路(pku2288)11
排列单词使相同位置的字符最多(pku2817WordStack)16
欧拉pizza店(pku3311)19
炮兵阵地(pku1185)22
数a的排列有多少个数可以被b整除(hustSCU-JCountingnumbers)25
最小矩形覆盖(2836RectangularCovering)28
4*N的面板用1*2的方格覆盖有多少种(pku3420)37
方格选数最大hdu2167
题目大意:
You'regivenanunlimitednumberofpebblestodistributeacrossanNxNgameboard(Ndrawn
from[3,15]),whereeachsquareontheboardcontainssomepositivepointvaluebetween10and
99,inclusive.
给你一个N*N个矩阵,要你选择若干个数,使得最后所选的数总和最大。
选数的规则是如果选了某个数,那么它的八个相邻方向的数都不能选。
本题用到stringstream这样处理起来比较方便。
状态压缩的DP。
#include
#include
usingnamespacestd;
intf[2][(1<<15)+10];
intnum[16][16],v[1<<15],vn,bit[20],line;
intst,N;
chartemp[1024];
voidinit()
{
inti;
memset(bit,0,sizeof(bit));
bit[0]=1;
for(i=1;i { bit[i]=bit[i-1]<<1; } } voidvalid() { inti,end,j; end=1< booltag; vn=0; for(i=0;i { tag=true; for(j=1;j { if((bit[j-1]&i)&&(bit[j]&i)) { tag=false; break; } } if(tag) { for(j=0;j { if(bit[j]&i) { f[0][i]+=num[0][j]; } } v[vn++]=i; } } } voiddfs(intx,ints,intt,intp) { if(x>=N) { if(f[st][t] { f[st][t]=f[st^1][s]+p; //printf("%d%d\n",t,f[st][t]); } return; } if(s&bit[x]) { dfs(x+2,s,t,p); } else { if(x==0) { if((s&bit[x+1])==0){ dfs(x+1,s,t,p); dfs(x+2,s,t|bit[x],p+num[line][x]); } else { dfs(x+3,s,t,p); } } else { if(((s&bit[x-1])==0)&&((s&bit[x+1])==0)) { dfs(x+1,s,t,p); dfs(x+2,s,t|bit[x],p+num[line][x]); } else dfs(x+1,s,t,p);//这个漏掉了,一直错,差了好久,分类一定要严密,哎 } } } voidout() { inti,end=1< for(i=0;i printf("%d",f[st][i]); printf("\n"); } voidDP() { memset(f[0],0,sizeof(f[0])); valid(); st=0; //out(); intj; intend=1< for(line=1;line<=N+1;line++) { st=1-st; memset(f[st],-1,sizeof(f[st])); for(j=0;j { if(f[1-st][v[j]]! =-1) { //printf("v[j]%d%d\n",v[j],f[1-st][v[j]]); dfs(0,v[j],0,0); } } //out(); } printf("%d\n",f[st][0]); } intmain() { inti; while(gets(temp)) { i=0; do { N=0; stringstreamss(temp); while(ss>>num[i][N]) N++; gets(temp); i++; if(temp[0]=='\0') break; }while(true); memset(num[N],0,sizeof(num[N])); init(); DP(); } return0; } 给奶牛安排牛场(pku2241) 问题描述: N个bull,M个barn,每个bull都有几个自己喜欢的barn,要求为每个bull分配一个barn,使得每个bull所分到的barn都是自己喜欢的,且每个barn至多只能容纳一个bull。 求合法的分配方案的种数。 (N,M<=20) 解法: 看到这个问题很容易想到搜索,复杂度O(n! ),实现很简单,在实际比赛中是一种可行的方法,但是和哈密顿回路问题一样,我们也可以通过状态DP来解决这个问题。 首先我们仍然尝试着分析搜索算法中的冗余问题,如下图: (图3)和(图4)分别表示两种搜索的中间状态,(图3)中红色方框1表示第1头bull住进了第1个barn,然后第2头bull住进了barn5,bull3住进了barn6,bull4住进了barn7,(图4)中4头bull所住的barn的集合是一样的,但是顺序略有不同。 如果还有bull没有分配barn,那么从(图3)和(图4)开始继续为剩下的bull分配barn的方案数肯定是一样的。 和哈密顿回路问题类似,用一个二元组表示前i个bull分配到用st表示的barn的集合中的所有的方案数,状态转移方程如下: 求出所有的f()后,只需要对所有i=n的f求和即可得到方案总数。 状态数为O(n*2^n),转移复杂度为O(n),总的时间复杂度是O(n^2*2^n)。 题目意思很简单,就是要给每个奶牛分配喜欢的球场,问你有多少中分配方法。 注意到N不大于20,所以对于很容易想到状态压缩的DP. 但是开始的时候由于DP的顺序不一样,花了两秒多。 后来看了别人的程序,也是跟我一样的状态压缩,但是他 的时间用了小于1秒。 后来经过自己的分析,发现原来我的DP的循环顺序跟他的不一样。 把自己的代码该过来之 后,200多毫秒就过了。 开始时的程序: Memory: 8284KTime: 2231MS Language: C++Result: Accepted voidDP() { inti,j,k,t,a; for(i=0;i f[0][i]=0; } f[0][0]=1; a=0; for(i=1;i<=N;i++){ a^=1; for(k=0;k f[a][k]=0; } for(j=1;j<=p[i];j++){ t=like[i][j]; for(k=0;k if(f[a^1][k]! =0&&(k&bit[t])==0){ f[a][(k|bit[t])]+=f[a^1][k]; } } } } intans=0; for(k=0;k ans+=f[a][k]; } printf("%d\n",ans); } 仔细分析这个程序,最后一次循环,浪费了判断,比如说在奶牛i喜欢球场1,那么状态k在第0位必须为0,但是有bit[M]/2个状态这些位都是1的,(这些状态都是无用的状态,但是这些状态都进行了判断)这样就浪费了很多次判断。 改过后的程序: (可以知道该过后的状态转移方程和原来其实是一样的)。 voidDP() { inti,j,k,t,a,b; for(i=0;i f[0][i]=0; } f[0][0]=1; a=0; b=1; for(i=1;i<=N;i++){ //for(k=0;k //f[b][k]=0; //} for(k=0;k { if(f[a][k]==0) { continue; } for(j=1;j<=p[i];++j) { t=like[i][j]; if((bit[t]&k)==0) { f[b][bit[t]|k]+=f[a][k]; } } f[a][k]=0; } a=1-a; b=1-b; } intans=0; for(k=0;k ans+=f[a][k]; } printf("%d\n",ans); } 可以发现,注释掉的那段代码也可以不要。 积木块(哈尔滨5085bircks) LittleWhiteboughtanewhouserecently.Shedoesn'tlikethedesignoftheflooranyway,soshedecidestodecoratethefloor.Nowshehasbricksofthe5shapesbelow,allwithaninfiniteamount. Brickscannotoverlapeachother,andyoucannotrotatethemtofitinthe"holes". Now,pleasetellLittleWhitehowmanyunitscanshecoverusingthesebricks. Input Foreverytestcase,youaregiventwointegersnandmindicatingthefloorisann*mrectangle consistingofn*m1*1grids.(1<=n<=100,1<=m<=6) Proceedtotheendoffile. Output Foreverytestcase,printanintegeronasingleline,representingthemaximumpossibleareathatcan becovered. SampleInput 14 23 32 44 SampleOutput 0 4 4 12 自己还是太弱了,因为忽略了一种很重要的地方,使得比赛时一直没做出来,直到比赛后才搞定。 这道题我是用状态压缩DP来做,因为每一行最多只有六格,所以选择行来进行状态DP,且采用三进制数字表示每一行。 对于每一行当中的每一格,若为2,则表示它为空且它前一行的该格也为空,若为1,则表示它为空,若为0,则表示它被占据了,举个例子,若一行有5个格子,那么11210(3进制数字)表示该行的第1、2、4个格子为空,第三个格子为空且它前一行的该格也为空,第五个格子被占据。 现在要在行与行之间进行状态的转化来计算某行为某个状态时它之前能填充的最大面积。 计算的具体过程如下,有第二行开始枚举放入的木块(当为第二行时只有两种木块可供选择,在第二行后有五种木块可供选择),其中from表示上一行的状态,to表示本行的状态,现在以放入题目中的第三种木块为例,因为要放入第三种木块,必须上一行这三个格子都为空且只需上一行这三个格子它们自己为空(不需要它们的上一行该格也为空),这也就是说要上一行三个格子的状态为111(对应10进制就是13),现在放入第三种木块,放入后,上一行三个格子被填满了,当前行对应的三个格子中,只有中间的格子被填充了,所以当前行三个格子的状态是101(对应10进制就是10),又因为放入木块后坐标位置会向后移动3,当前被填充的面积会增加4,所以当放入第三种木块,要这样写状态转移函数dfs(x+3,from*27+13,to*27+10,n+4);。 放入其它的木块同理以及不放入木块同理。 现在再说说这个题特别要注意的地方,我比赛时就是忽略了这个地方才一直wa。 就是当放入一个木块后,若使得现在要开始放入位置的前一个格子的状态为2(前一格为空且前一格对应的上一行的格子也为空),那么对于木块2、3、5(数字代表第几种木块),不应该还是就这当前位置放入他们,而是还要考虑把他们向左退一格放入,这样才会节省空间,保证最后得到最优解。 可以结合下面图片理解我说的意思,即不应该仅向图片1一样放入3,而是还要考虑向图片2一样放入3。 那么此时转移函数应该这样写if(to%3==2)dfs(x+2,from*9+4,(to-1)*9+1,n+4);。 最后再说说怎么记录状态进行状态转移的问题,这点很重要,因为若没记录好状态,则转移是不成功的,最后也不会得到正确的答案。 对于每放入一种木块,from的改变是固定的,那么to呢,若to也仅仅对应一种的话,那么在一定要处理好行间转换的问题,若to对应所有可能的解,那么对于放入每种木块,要把所有可能的转移函数都得写上。 先说说第二种,在第二种的情况下,每次放入木块后,把to对应的空格子也要分别尝试填充,这样就记录所以可能的状态,使得在求以后行的状态中,可用之前记录的状态来推导,并且计算完每一行后0就对应了最优解的状态。 鉴于第二种情况记录的无效态多且代码量大,这里再说说第一种情况,若对应每个from只记录一个to的话,那么在行间转换时,对于每一格,若它上一个的状态为2,则它也为2,若为1,则它可为2,也可为1,若为0,则它为1,这样写会大大省了代码量,也省去了一些无效状态,但要注意一点就是计算完每一行后,最优解要枚举出该行的所有状态才能算出。 对于记录状态转移的第二种做法,对应下面的代码1(81MS),对于第一种做法,对应代码2(55MS)。 关于这题网上已经找到了解题报告,不过开始的时候看解题报告一直没有理解,特别是那个(to%3=2)那边的递归部分。 还有关于本题,如果没有看别人的代码,直接写的话,可能是枚举前一行的所有有效状态,然后从每个有效的状态开始DP,(跟在自己以前写的状态DP很像)不过 这样很麻烦,对于本题其实跟以前的那个1*2的木块其实是类似的,可以同时dfs生成from和to两个状态这样递推。 对于本题,例如: 木块3,为什么只要算前一行的三个方格为空的情况就可以了,而不用枚举所有的8种状态,当前格为空,同时前格为空或不为空的情况,原因很 简单,那就是前2行为空的状态等定不会比只要前一行为空的方格数多,因为要算最后,所以只要前1行的为空就可以了。 还有关于关于为什么别人写的解题报告里面有to%3==2这个递归,原因很简单,就是因为最后(第5中)拼图的关系,放了第5中方格,不能直接就x+2,对于 x+1还有空出两个方格,可以放2,3,4这三种方格,所以程序中有to%3==2的递归。 但是这样有很多重复的递归,本人通过改进, 把第5种拼图和第2,3,4种拼图合并来处理,这样就不会有重复的递归了,时间又提高了一点。 还有一点值得注意: 就是对于不放的情况: 少了这个递归就错了: dfs(x+1,from*3+1,to*3+1,n);请认真想想为什么! ! #include #include usingnamespacestd; #defineMAXN100 #defineMAXC6 intpw[MAXC+1]={1}; inta[2][2200],rec[101][7]; intp,c; voiddfs(intx,intfrom,intto,intn) { if(x>c+1)return; if(x==c+1){if(a[1-p&1][from]+n>a[p&1][to])a[p&1][to]=a[1-p&1][from]+n;return;} dfs(x+3,from*27+13,to*27+10,n+4);//3 dfs(x+3,from*27+13,to*27+4,n+4);//4 if(p>=3) { dfs(x+2,from*9+7,to*9+1,n+4);//1 dfs(x+3,from*27+16,to*27+10,n+5);//2 dfs(x+2,from*9+8,to*9+2,n+4);//5 dfs(x+4,from*81+76,to*81+10,n+8);//合并5,3 dfs(x+4,from*81+79,to*81+10,n+9);//合并5,2这边是加9 dfs(x+4,from*81+76,to*81+4,n+8);//合并5,4 } dfs(x+1,from*3+2,to*3+2,n); dfs(x+1,from*3+1,to*3+2,n); dfs(x+1,from*3+1,to*3+1,n); dfs(x+1,from*3,to*3+1,n); } intfind_ans() { intans=0;
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 状态 压缩 DP 个人 整理 总结