状态压缩

来源:互联网 发布:sql server桌面图标 编辑:程序博客网 时间:2024/05/17 23:34

算法就不解释了,直接上题

题目:http://codevs.cn/problem/1647/

这老经典的题了嚎~~~

首先,我们来看看m,(m<=10),

这样,我们就可以考虑动态规划

并且,如果我们把它划分成n层的话,那么第i层只跟第i-1层和第i-2层有关。

设第i层的决策状态为ki,则

a[i,ki,ki-1]=max(a[i-1,ki-1,ki-2]+num[ki])

对于每个状态,可以考虑状态压缩,

事先把每个状态都预处理一遍,储存在二进制数里

二进制数中'0'表示没有放置炮台,'1'表示放置了炮台。

再把每一层的地形存在二进制数里,'0'表示平原,'1'表示山地。

设map[i]为第i层的地形,

这样,对于第i(i>=3)层,首先枚举ki-2,使得k[i-2] & map[i-2]=0

再枚举ki-1,使得k[i-1] & map[i-1]=0且k[i-2] & k[i-1]=0,

再枚举ki,使得k[i] & map[i]=0且k[i] & k[i-1]=0且k[i] & k[i-2]=0(反过来也没关系啦)

代码上~:

#include <cstdio>#include <iostream>using namespace std;int f[105][65][65],map[105],zbk[1050],num_zbk[1050];char ch[150];int main(){int i,j,k,l,n,m,x,num,ans=0;freopen("a.txt","r",stdin);scanf("%d%d",&n,&m);for (i=1;i<=n;i++){scanf("%s",&ch);for (j=1;j<=m;j++)if (ch[j-1]=='H')map[i]|=1<<(j-1);//map[i]记录第i行}num=0;for (i=0;i<=(1<<m)-1;i++)//i=0~111..1{if ((i&(i<<1)) || (i&(i<<2)))//不能有相邻1~2个1 continue;x=0;for (j=1;j<=m;j++)//找i的二进制1个数 if (i&(1<<(j-1)))x++;zbk[++num]=i;//zbk[]记录每行的二进制状态 num_zbk[num]=x;//状态下该行炮台个数 }//前面为预处理。。。 for (i=1;i<=num;i++)//第一行 if ((map[1]&zbk[i])==0)f[1][i][1]=num_zbk[i];//第二行for (i=1;i<=num;i++) //枚举两层状态:第二层 if ((map[2]&zbk[i])==0)for (j=1;j<=num;j++)//第一层 if ((map[1]&zbk[j])==0 && (zbk[i]&zbk[j])==0)f[2][i][j]=max(f[2][i][j],f[1][j][1]+num_zbk[i]);for (i=3;i<=n;i++)//3~n层 for (j=1;j<=num;j++)//第i层状态if ((map[i]&zbk[j])==0)for (k=1;k<=num;k++)//i-1层状态if ((map[i-1]&zbk[k])==0 && (zbk[j]&zbk[k])==0)for (l=1;l<=num;l++)//i-2层状态if ((map[i-2]&zbk[l])==0 && (zbk[k]&zbk[l])==0 && (zbk[l]&zbk[j])==0)f[i][j][k]=max(f[i][j][k],f[i-1][k][l]+num_zbk[j]);for (i=1;i<=num;i++){for (j=1;j<=num;j++)ans=max(ans,f[n][i][j]);}printf("%d",ans);return 0;}

熟悉了算法如何写以后,再来看看这道题:
题目:http://www.gdfzoj.com/oj/contest/254/problems/3
看到n<=20我就试着做了下状压,发现可以实现,结果只有40。。。(第一次还忘了删文操。。。),才发现看错题了。。。
所以所以,看题!看题!!看题!!!(重要的事情说三遍)
思路不多说,直接上代码:
代码细节:
1)此处find≈lower_bound(pair不能用lower_bound)
找第一个x
2)注意pair定义,使用等。。。
#include <cstdio>#include <algorithm>using namespace std;const int maxSize=23;int a[maxSize],n;pair <long long,long long> c[1<<maxSize];//写sort不用cmp,比map好 long long find(long long x){int l=1,r=(1<<n)-1,mid;while (l!=r){mid=(l+r)/2;if (c[mid].first>=x)//写等于可以找第一个x r=mid;elsel=mid+1;}return l;}int main(){long long i,j,sum,ans=0;freopen("a.txt","r",stdin);scanf("%d",&n);for (i=0;i<n;i++)scanf("%d",&a[i]);for (i=1;i<(1<<n);i++)//预处理一遍和 {sum=0;for (j=0;j<n;j++)if (i&(1<<j))sum+=a[j];c[i]=make_pair(sum,i);}sort(c+1,c+i);//排序(按和) for (i=1;i<(1<<n);i++){sum=0;for (j=0;j<n;j++)//再找一遍和(排序后的,与前面预处理不重复)if (i&(1<<(j)))sum+=a[j];if (sum%2==1)continue;for (j=find(sum/2);c[j].first==sum/2;j++)//二分找sum/2 if ((c[j].second&i)==c[j].second && c[j].second!=i)//sum/2 的状态属于sum状态子集 {ans++;break;//看题!!!!!!!!! }}printf("%lld",ans);return 0;}

T3:http://www.lydsy.com/JudgeOnline/problem.php?id=1087
这题跟第一题很像。。。
注意预处理、答案大小(long long)
#include <cstdio>#include <algorithm>using namespace std;int zbk[1050],num_zbk[1050];long long f[15][1050][150];//f[i,j,k]记录第i层为j状态,前i行放k个的方案数 //要开ll,可以预估一下答案大小 int main(){int i,j,k,n,k1,num=0,x,l;long long ans;freopen("a.txt","r",stdin);scanf("%d%d",&n,&k1);for (i=0;i<(1<<n);i++)//预处理 {if (i&(i<<1))continue;x=0;for (j=0;j<n;j++)if (i&(1<<j))x++;zbk[++num]=i;num_zbk[num]=x;}//for (i=1;i<n;i++)//for (j=1;j<=num;j++)//f[i][j][0]=1;//初始化 k=0时的情况 for (i=1;i<=num;i++)//第一层 if (num_zbk[i]<=k1)f[1][i][num_zbk[i]]=1;for (i=2;i<=n;i++)//第2~n层 {for (j=1;j<=num;j++)//第i层状态 {for (k=1;k<=num;k++)//第i-1层状态 if (!(zbk[j]&(zbk[k]<<1)) && !(zbk[j]&zbk[k])&&!(zbk[k]&(zbk[j]<<1)))for (l=num_zbk[j];l<=k1;l++)//循环'k'  f[i][j][l]+=f[i-1][k][l-num_zbk[j]];}}ans=0;for (i=1;i<=num;i++)ans+=f[n][i][k1];printf("%lld",ans);//记得打lldreturn 0;}


原创粉丝点击