【洛谷 P1879】【[USACO06NOV]玉米田Corn Fields】

题目:

链接

思路:

Q:如何想到是状压DP

A:那是因为(我看了标签\(1 ≤ M ≤ 12; 1 ≤ N ≤ 12\)\(2 ^ {12}\) 不过才。。。(Win7计算器使用中)\(4096\)嘛! 然后如果用状压DP也可以优化时空


确定状态:

\(f_{i,j}\) 表示第\(i\)行的方案(对,方案,这是方案而答案是方案数)是\(j\)(是一个二进制数,用十进制来存储,第\(k\)位是\(1/0\)(二进制)表示\(/\)不选)时的方案数。

确定转移方程:

声明:下面的\(j,k\)都是一个合法的方案

设已经进行到\(i\)行,此时的方案是\(j\),上一行的方案是\(k\)

有一个特殊条件(边界):\(i = 1\)

既然是第一行,那么它的所以合法方案都是正确的,所以边界是:
\[\Large {f_{1,j} = 1}\]

也可以很容易地想到本行的合法方案的方案数是上一行的所有合法方案数,也就是:

\[\Large {f_{i,j} = f_{i,j}+f_{i - 1,k}}\]

代码:

声明:那个优化可能并无卵用。。。

const int N = 15;
int n, m;
int f[N][(1 << N)];
int st[1 << N];   //一个小小的优化数组
int a[N];
int tot;

void _init()     //一个小小的优化,判断此方案  在这一行  是不是合法的
{
    for (int i = 0; i < (1 << m); i++)
    {
        if (i & (i << 1)) continue;
        st[++tot] = i;
    }
}

int main()
{
    scanf ("%d%d", &n, &m);
    for (int j = 1; j <= n; j++)
        for (int i = m - 1; i >= 0; i--)
        {
            int x;
            scanf ("%d",&x);
            a[j] += (x << i);  //本行的方案(可能不是合法)
        }
    _init();        //开始优化
    for (int i = 1; i <= tot; i++)         //边界条件
    {
        if (!((st[i] | a[1]) == a[1]))continue;  //是否合法
        f[1][st[i]] = 1;
    }
    for (int i = 2; i <= n; i++)
    {
        for (int j = 1; j <= tot; j++)
        {
            if (!((st[j] | a[i]) == a[i]))continue;   //判断合法
            for (int k = 1; k <= tot; k++)
            {
                if (!((st[k] | a[i - 1]) == a[i - 1]))continue;  //同上条注释
                if (st[j] & st[k]) continue;
                f[i][st[j]] += f[i - 1][st[k]];      //转移
                f[i][st[j]] %= 100000000;
            }
        }
    }
    int ans = 0;
    for (int j = 1; j <= tot; j++)             //答案
        ans += f[n][st[j]], ans %= 100000000;
    printf ("%d", ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/GJY-JURUO/p/12037232.html