洛谷P1879 [USACO06NOV]玉米田Corn Fields
入门题
/* 洛谷P1879 n*m矩阵 有些位置可选 有些不可选 不能选择两块相邻的土地 状压裸题:记录状态->判断矛盾->处理答案 */ #include<bits/stdc++.h> using namespace std; #define N 13 #define mod 100000000 int a[N][N],dp[N][1<<N],sta[1<<N]; bool get(int i)//判断是否有连续的 1 { if(i&(i<<1)) return false; return true; } int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ scanf("%d",&a[i][j]); if(a[i][j]) a[i][j]=0; else a[i][j]=1; sta[i]=(sta[i]<<1)+a[i][j];//sta为每一行的状态 } for(int i=0;i<(1<<m);i++) if(!(i&sta[1])&&get(i)) dp[1][i]++;//预处理第一行 for(int i=2;i<=n;i++) for(int j=0;j<(1<<m);j++) if(!(j&sta[i])&&get(j)){ for(int k=0;k<(1<<m);k++){ if((k&sta[i-1])||(j&k)||!get(k)) continue;//j&k判断是否两行有矛盾状态 dp[i][j]=(dp[i][j]+dp[i-1][k])%mod; } } int ans=0; for(int i=0;i<(1<<m);i++) ans=(dp[n][i]+ans)%mod; printf("%d\n",ans); } /* 2 3 1 1 1 0 1 0 */
洛谷 P1896 [SCOI2005]互不侵犯
/* 洛谷P1896 题意:在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。 国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子 思路:判断是否有相邻的1 判断是否和上一行隔一个有一个1 */ #include<bits/stdc++.h> using namespace std; #define ll long long #define N 10 ll dp[N][1<<N][N*N];//多定义一维x 表示到这一行已经放了多少个国王 int get(int x) { int ans=0; while(x){ if(x&1) ans++; x>>=1; }//求这一行的 1 的个数 return ans; } bool check(int x) { if(x&(x<<1)) return false;//求有没有相邻的 1 return true; } int main() { int n,m; scanf("%d%d",&n,&m); for(int i=0;i<(1<<n);i++) if(check(i)) dp[1][i][get(i)]++; for(int i=2;i<=n;i++) for(int j=0;j<(1<<n);j++) if(check(j)){ for(int k=0;k<(1<<n);k++){ if(!check(k)||(j&k)||j&(k<<1)||(j<<1)&k) continue; for(int x=0;x<=m;x++) dp[i][j][x+get(j)]+=dp[i-1][k][x]; } } ll ans=0; for(int i=0;i<(1<<n);i++) ans+=dp[n][i][m]; printf("%lld\n",ans); }
洛谷P2704[NOI2001]炮兵阵地
/* 炮兵阵地 普通状压 */ #include<bits/stdc++.h> using namespace std; #define N 105 #define M 12 int a[N][N],sta[1<<M],dp[2][1<<M][1<<M],get[1<<M]; bool check1(int x) { if( ( (x<<1)&x ) || ( (x<<2)&x ) ) return false; return true; } char op[15]; int main() { int n,m,ans=0; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%s",op); for(int j=0;j<m;j++){ if(op[j]=='H') a[i][j]=1; sta[i]=(sta[i]<<1)+a[i][j];//()!!!!!位运算的级别真的很低的 每当遇到不知道是什么问题的时候 考虑给位运算加个括号 } } for(int i=0;i<(1<<m);i++){//预处理每一个状态对应炮兵的个数 int now=i,cnt=0; while(now){ if(now&1) cnt++; now>>=1; } get[i]=cnt; } for(int i=0;i<(1<<m);i++) if( check1(i) && !(i&sta[1]) ) dp[1][i][0]=get[i];//预处理第一行 for(int i=0;i<(1<<m);i++)//预处理前两行 for(int j=0;j<(1<<m);j++) if( !(i&sta[1]) && !(j&sta[2]) && check1(i) && check1(j) && !(i&j) ) dp[0][j][i]=max(dp[0][j][i],dp[1][i][0]+get[j]); //看起来是10^11 但实际上远远达不到!! for(int i=3;i<=n;i++) for(int j=0;j<(1<<m);j++) if( check1(j) && !(j&sta[i]) ){//自己一行的状态不矛盾+与别人状态不矛盾+没有在山峰上放了炮兵 for(int k=0;k<(1<<m);k++)//枚举上一行 if( !(k&sta[i-1]) && check1(k) && !(k&j) ){ for(int p=0;p<(1<<m);p++)//枚举上上行 if( !(j&p) && !(p&sta[i-2]) && check1(p) && !(p&k) ) dp[i&1][j][k]=max(dp[i&1][j][k],dp[(i&1)^1][k][p]+get[j]); } } for(int i=0;i<(1<<m);i++) for(int j=0;j<(1<<m);j++) ans=max(ans,dp[n&1][i][j]); printf("%d\n",ans); } /* 6 5 PPPPP PPPPP PPPPP PPPPP PPPPP PPPPP */
洛谷P2051 [AHOI2009]中国象棋
/* 洛谷P2051 题意:n*m的矩阵 每列最多放两个格子 每行最多放一个格子 求方案数 思路:状压,但n m太大 不能将所有状态压入 又 发现不必知道每一个详细状态 只需要知道每一行每一列放了多少个棋子 于是考虑维护列所放的状态(只维护放的个数) 枚举行进行dp转移 即固定行只选两个 对列进行转移 */ #include<bits/stdc++.h> using namespace std; #define mod 9999973 #define ll long long #define N 105 //加强版状压 难点在于dp的定义 //定义 dp[i][j][k]为第i行 放了j列只有一个的格子 放了k列有两个的格子 ll dp[N][N][N]; ll C(int n) { return (n-1)*n/2; } int main() { int n,m; scanf("%d%d",&n,&m); dp[0][0][0]=1; for(int i=0;i<=n-1;i++)//1不好先初始化 就由i转移到i+1 for(int j=0;j<=m;j++) for(int k=0;j+k<=m;k++)//注意范围!! j+k<=m if(dp[i][j][k]){//排除无用状态 //每一行只能放两个棋子 分类讨论 dp[i+1][j][k]=(dp[i+1][j][k]+dp[i][j][k])%mod;//什么都不放 if(m-j-k>=1) dp[i+1][j+1][k]=(dp[i+1][j+1][k]+dp[i][j][k]*(m-j-k))%mod;//放一个在没有棋子的一列 if(j>=1) dp[i+1][j-1][k+1]=(dp[i+1][j-1][k+1]+dp[i][j][k]*j%mod)%mod;//放一个在有一个棋子的一列 if(m-j-k>=2) dp[i+1][j+2][k]=(dp[i+1][j+2][k]+dp[i][j][k]*C(m-j-k)%mod)%mod;//放两个在没有棋子的两列 if(j>=2) dp[i+1][j-2][k+2]=(dp[i+1][j-2][k+2]+dp[i][j][k]*C(j)%mod)%mod;//放两个在有棋子的两列 if(m-j-k>=1&&j>=1) dp[i+1][j][k+1]=(dp[i+1][j][k+1]+dp[i][j][k]*(m-j-k)*j); //一个放没有棋子的 一个放有一个棋子的 } ll ans=0; for(int i=0;i<=m;i++) for(int j=0;i+j<=m;j++) ans=(ans+dp[n][i][j])%mod; printf("%lld\n",ans); }
待更新。。。