首页 > 代码库 > codeforces 277.5 div2 F:组合计数类dp
codeforces 277.5 div2 F:组合计数类dp
题目大意:
求一个 n*n的 (0,1)矩阵,每行每列都只有两个1 的方案数
且该矩阵的前m行已知
分析:
这个题跟牡丹江区域赛的D题有些类似,都是有关矩阵的行列的覆盖问题
牡丹江D是求概率,这个题是方案数,也比较相似。。
这种题中,因为只要求方案数。。我们只要关注几行几列有几个1,而不必要关注具体的位置
题解:
行列都需要处理,因此考虑记录列的状态,然后一行一行的转移
最暴力的方程:
dp[i][j][k][t] 表示已经确定了 i 行 有 j列已经有两个1,有k列只有一个1,有t列一个1也没有的方案数
很显然 第i+1行的两个1 只能放在k列中 或者 t列中
状态转移方程乘上组合数也是很好写的
但是这个方程显然过于暴力。。需要优化
首先,显然 j+k+t=n 因此 t 就不用枚举了
方程变为n3,由于cf服务器很强大,已经可以过了。。
然后又发现 ,前 i 行 应该有且只有 2i 个1 ,所以显然 k=2* i - j
k也不用枚举了。。
最终得到一个二维的dp
代码如下:
#include <iostream>#include <stdio.h>#include<string.h>#include<algorithm>#include<string>#include<ctype.h>using namespace std;#define MAXN 10000int n,m,mod;int dp[2][501];char s[501];int num[501];void ini(){ memset(num,0,sizeof(num)); //memset(dp,0,sizeof(dp)); for(int i=1;i<=m;i++) { scanf("%s",s); for(int i=0;i<n;i++) { num[i]+=s[i]-‘0‘; } } int j=0; for(int i=0;i<n;i++) { if(num[i]==2) j++; } dp[m%2][j]=1;}void solve(){ for(int i=m+1;i<=n;i++) { for(int j=0;j<=n;j++) { if(!dp[(i-1)%2][j]) continue; int k=2*(i-1)-2*j; int t=n-j-k; if(k<0) break; if(j<=n-2&&k>=2) { dp[i%2][j+2]+=(long long)dp[(i-1)%2][j]*k*(k-1)/2%mod; dp[i%2][j+2]%=mod; } if(t>=2) { dp[i%2][j]+=(long long)dp[(i-1)%2][j]*(t)*(t-1)/2%mod; dp[i%2][j]%=mod; } if(t&&k) { dp[i%2][j+1]+=(long long)dp[(i-1)%2][j]*(t)*k%mod; dp[i%2][j+1]%=mod; } dp[(i-1)%2][j]=0; } } printf("%d\n",dp[n%2][n]);}int main(){ while(scanf("%d%d%d",&n,&m,&mod)!=EOF) { ini(); solve(); } return 0;}
codeforces 277.5 div2 F:组合计数类dp
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。