2017.10.4 DP 解题报告

来源:互联网 发布:信息群发软件 编辑:程序博客网 时间:2024/06/14 01:04

Description

一个n*m的01矩阵,每一行给出li和ri
现在给出限制条件,对于每一行
第 1~li列恰好有 1个 1,第 ri~m列恰好有 1个 1。
对于每一列,至多有 1个 1。

【解题报告】

都写在代码里了
代码如下:

#include<cstdio>#include<cstring>#include<algorithm>#define rep(i,a,b) for(i=a;i<=b;i++)using namespace std;const int N=3010;const int maxn=3000;const int mod=998244353;int c[N][N],dp[N][N];int cj[N];int sl[N],sr[N],s[N];int cl[N],cr[N];int main(){    freopen("matrix.in","r",stdin);    freopen("matrix.out","w",stdout);    int l,r;    int n,m;    for(int i=0;i<N;++i)    {        c[i][0]=1;        for(int j=1;j<=i;++j) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;    }    cj[0]=1;for(int i=1;i<N;++i) cj[i]=1ll*cj[i-1]*i%mod;    scanf("%d%d",&n,&m);    for(int i=1;i<=n;++i)    {        scanf("%d%d",&l,&r);        cl[l]++;        cr[r]++;    }    for(int i=1;i<=m;++i)     {        sl[i]=sl[i-1]+cl[i];        sr[i]=sr[i-1]+cr[i];        s[i]=sl[i]+sr[i];    }    dp[0][0]=1;    for(int i=1;i<=m;++i)    {        int k,c1,c2;        for(int j=0;j<=sr[i-1];++j)        {            //用dp[i][j]表示做到前i列,对于已经跨过的左区间我们已经填了1了,对于碰到的右区间我们已经填了一部分1,但还有j和右区间待定             if(!dp[i-1][j]) continue;            k=i-1-s[i-1]+j;//当前列左边留下的可以填的空             if(k<0) continue;            //在更新到当前列时我们会碰到一些右区间,我们可以选择一个区间填上1,也可以把他们全部待定             c1=cl[i]<=k?1ll*c[k][cl[i]]*cj[cl[i]]%mod:0;            //如果我们填上一个1的之前的待定填法             c2=cl[i]<=k+1?1ll*c[k+1][cl[i]]*cj[cl[i]]%mod:0;            //如果我们全部待定,则对于之前的待定区间,有k+1个空,填上cl(i)个1的方案             dp[i][j+cr[i]]=(dp[i][j+cr[i]]+1ll*dp[i-1][j]*c2)%mod;            //多有新加入的右区间全部待定             if(j+cr[i]) dp[i][j+cr[i]-1]=(dp[i][j+cr[i]-1]+1ll*dp[i-1][j]*c1%mod*(j+cr[i]))%mod;            //填上一个右区间         }    }    printf("%d",dp[m][0])%mod;}
原创粉丝点击