Luogu-P1879 [USACO06NOV]玉米田Corn Fields
题目
测试得分: 100
主要算法 : 状压DP(二进制优化)
题干:
状压DP板子
分析
1.就是像之前的互不侵犯一样,只是合法状态还要判断要与玉米地的贫瘠程度,是否可以种植玉米有关
2.向互不侵犯一样,判读是否两两相邻
3.好了上代码了
代码
#include<stdio.h> #include<stdlib.h> #define FORa(i,s,e) for(int i=s;i<=e;i++) #define FORs(i,s,e) for(int i=s;i>=e;i--) #define gc pa==pb&&(pb=(pa=buf)+fread(buf,1,100000,stdin),stdin)?EOF:*pa++ #define File(name) freopen(name".in","r",stdin);freopen(name".out","w",stdout); using namespace std; char buf[100000],*pa,*pb; inline int read(); const int SUM=4096,N=12,MOD=100000000; int n,m,ans,f[N+1][SUM+1]; /*f[i][j]代表的是前i行且第i行的状态为j满足的方案数 j原本是一维的有n个元素的数组,代表的是第i行的状态,但是对于状态只有01两种选择,放与不放 所以可以将01串看作二进制数,则每一个状态对应一个唯一的十进制数,这就是这一题的核心思想状压 还有就是要符合这片土地是否可以种植 */ struct Node{ int num,s[SUM+1]; }a[N+1]; void Pretreatment(int i,int t) { for(int j=0,jn=(1<<m);j<jn;j++)//枚举每一行的状态 { if((j&(j<<1))||(j&(j>>1))||(j&t)) continue; /*如果有左右两个相邻都种植corn的话就不合法 还有就是要符合这片土地是否可以种植 原本是为0的不能种植,但因为位运算的一些操作是对于1敏感 所以此处有一种特殊的解决方法,就是将土地初始状态反向转化,0变成1,1变成0 所以与现在枚举的状态如果有交集,直接忽略*/ a[i].s[++a[i].num]=j;//累加每一行的状态 } } void Dp() { FORa(i,1,a[1].num) f[1][i]=1; FORa(i,2,n) FORa(j,1,a[i].num)//枚举这一行所有合法状态 FORa(k,1,a[i-1].num)//枚举下一行所有合法状态 { if(a[i].s[j]&a[i-1].s[k]) continue;//上下两行不能有同一列号都种植玉米的情况 f[i][j]+=f[i-1][k];//合法则累加 } FORa(i,1,a[n].num) ans=(ans+f[n][i])%MOD;//累加答案 printf("%d",ans); } int main() { scanf("%d%d",&n,&m); FORa(i,1,n) { int t=0,x; FORa(j,1,m) scanf("%d",&x),t=(t<<1)+1-x; /*要符合这片土地是否可以种植 原本是为0的不能种植,但因为位运算的一些操作是对于1敏感 所以此处有一种特殊的解决方法,就是将土地初始状态反向转化,0变成1,1变成0 所以与现在枚举的状态如果有交集,直接忽略*/ Pretreatment(i,t); } Dp(); return 0; } inline int read() { register char c(gc);register int f(1),x(0); while(c<'0'||c>'9') f=c=='-'?-1:1,c=gc; while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=gc; return x*f; } /*2 3 1 1 1 0 1 0*/
总结:
1.DP状态的确定
2.预处理筛选不合法状态