洛谷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);
}

待更新。。。

posted on 2019-07-22 21:08  rua-rua-rua  阅读(558)  评论(0编辑  收藏  举报