第十周小结

  这周继续学习了dp的专题,主要看了状态dp和概率dp和练了一点数位dp的题。看过状态dp的感受就是,难,不好理解。在一道题需要考虑的状态很多时,我们就可以用到状态dp,通常用二进制来表示状态。当需要表示一个集合有哪些元素时,就可以用2进制的一个整数表示。

  有的题,状态比较好表示,像 P1879 [USACO06NOV]Corn Fields G,题意是一个矩阵里有很多格子,每个格子有两种状态,可以放牧和不可以放牧,可以放牧用1表示,否则用0表示,在这块牧场放牛,要求两个相邻的方格不能同时放牛,求放牛的方案。用dp表示的话,我们可以设一个二维数组,一个维度是用来表示第几行的,一个维度就是用来表示哪一行的状态的。接下来到重点,考虑状态怎样表示,用状态压缩,二进制来表示某个状态。比如,0101代表的就是1、3列不放牛,2、4列放牛。再找到状态转移方程:dp [ i ] [ j ] + = dp [ i − 1 ] [ f ],就可以解题了。

#include <bits/stdc++.h>
const int N = 13;
const int M = 1;
const int mod = 100000000;
int st[M],a[M];
int dp[N][M];
using namespace std;
bool judge1(int x)
{  return (x&(x<<1)); }
bool judge2(int i,int x)
{  return (a[i]&st[x]);}
int main()
{   int n,m,x;
    while(cin>>n>>m)
    {   memset(st,0,sizeof(st));
        memset(a,0,sizeof(a));
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++)
        { for(int j=1;j<=m;j++){
            cin>>x;
            if(x==0)  a[i]+=(1<<(j-1));
          }
        }
    int k=0;
    for(int i=0;i<(1<<m);i++)
     if(!judge1(i))  st[k++]=i;
    for(int i=0;i<k;i++)
    { if(!judge2(1,i)) dp[1][i]=1; }
    for(int i=2;i<=n;i++)
    { for(int j=0;j<k;j++)
      { if(judge2(i,j)) continue;
        for(int f=0;f<k;f++)
        { if(judge2(i-1,f)) continue;
          if(!(st[j]&st[f])) dp[i][j]+=dp[i-1][f];
         }
      }
    }
    int t=0;
    for(int i=0;i<k;i++){
        t+=dp[n][i];
        t%=mod;
    }
    cout<<t<<endl;
    }
    return 0;
}

  有些较难的状态dp题,状态表示就不是那么好找了,看了一些题,看的雨里雾里的,没弄明白。还有位运算,之前没接触过,这周刚学的,用起来还有些不熟练。感觉状态dp偏难点,题目都不好理解,可能再多看些题能开窍吧。在状态dp上耗了较多时间没有进展后,我打算先把他放一放,看看概率dp转换下思维。概率dp开了一点头,理解起来倒是不难,正好这学期开了概率统计这门课,知识有重叠的部分。下周再接着看状态dp的题,同时接触下概率dp的题。再接再厉。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值