CodeForces-489f

题目意思是给一个m*n的01矩阵,将剩余的n-m行01矩阵填满,并保证最后的n*n的矩阵每一行和每一列都有且仅有两个1,问有多少种填法。
对于每一行,我们都可以从上一行的所有状态中推出当前这一行填01的方法种数。对于填哪两列其实我们并不用关心,只需要记录到当前行由几个列选了0个1和1个1即可。这样配合组合数就可以进行 O(n3) 的dp转移。
但是实际上我们知道了当前行有多少个“0”(即0个1),我们便可以求出有多少个“1”,因为01矩阵1的总个数是确定的。这样dp的时间复杂度就变成了 O(n2).

#include 
using namespace std;
int n, m, mod;
typedef long long ll;
const int maxn = 1000 + 5;
char s[maxn][maxn];
ll d[2][500+5][500+5];
ll C[maxn][maxn];
int main(int argc, char const *argv[])
{
    scanf("%d%d%d", &n, &m ,&mod);
    C[1][0] = C[1][1] = 1;
    for(int i = 2; i < 600; ++i) {
        C[i][0] = 1;
        for(int j = 1; j < 600; ++j) {
            C[i][j] = (C[i-1][j]+C[i-1][j-1])%mod;
        }
    }
    for(int i = 1; i <= m; ++i) {
        scanf("%s", s[i] + 1);
    }
    int a  = 0 ,b = 0, c = 0;
    for(int j = 1; j <= n; ++j) {
        int cnt = 0;
        for(int i = 1; i <= m; ++i) {
            if(s[i][j] == '1') cnt++;
        }
        if(cnt == 0) a++;
        else if(cnt == 1) b++;
        else c++;
    }
    d[0][a][b] = 1;
    int x = 0;
    for(int i = m+1; i <= n; ++i) {
        x ^= 1;
        for(int j = 0; j <= n; ++j) {
            int k = ((n-i+1) << 1) - (j << 1);
            if(k < 0 || k > n) continue;
            if(j >= 2){
                (d[x][j-2][k+2] += d[x^1][j][k] * C[j][2] % mod) %= mod;
            }
            if(j >= 1 && k >= 1) {
                (d[x][j-1][k] += d[x^1][j][k] * j * k % mod) %= mod; 
            }
            if(k >= 2){
                (d[x][j][k-2] += d[x^1][j][k] * C[k][2] % mod) %= mod;
            }
        }
    }
    cout << d[x][0][0] << endl;
    return 0;
}

你可能感兴趣的:(动态规划,数学)