【CQOI2008】矩阵的个数题解

Description
Solution
一道数据很水的dp,也是唯一一道有多个测试点的题目。

设f[i][j][k]表示第i行,当前第一列总和为j,第二列总和为k的方案数。很多人会问:那么第三行呢?只要我们细心观察可以发现,当我们知道第一列和第二列的总和时,我们可以直接求出第三列的总和,用一个前缀和就好了。

可得状态转移方程:
为当前第一列要填的数,a2为当前第二列要填的数。

在此声明一点:不需要打前缀和,我比赛时本来就是五重循环就对了,结果因为打了前缀和就错了,到现在都不知道为什么错!

期望得分:100
实际得分:20

#include<cstdio>
#define ll long long
using namespace std;
int n,c1,c2,c3;
int a[201],s[201];
ll f[201][126][126];
const ll mo=1e17;
int max(int x,int y) {
	if(x>y)return x;
	return y;
}
int min(int x,int y) {
	if(x<y)return x;
	return y;
}
int read() {
	int s=0;
	char ch=getchar();
	while(ch<48||ch>57)ch=getchar();
	while(ch>=48&&ch<=57)s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
	return s;
}
int main() {
	scanf("%d%d%d%d",&n,&c1,&c2,&c3);
	for(register int i=1;i<=n;i++) {
		scanf("%d",&a[i]);
		s[i]=s[i-1]+a[i];
	}
	f[0][0][0]=1;
	for(register int i=1;i<=n;i++)
		for(register int j=0;j<=min(s[i],c1);j++)
			for(register int k=max(0,s[i]-j-c3);k<=min(s[i]-j,c2);k++)
				for(register int a1=0;a1<=min(j,s[i-1]);a1++)
					for(register int a2=max(max(0,j-a1+k-a[i]),s[i-1]-a1-c3);a2<=min(k,s[i-1]-a1);a2++)
						f[i][j][k]=(f[i][j][k]+f[i-1][a1][a2])%mo;
	printf("%lld",f[n][c1][c2]);
}

猜你喜欢

转载自blog.csdn.net/MZHjr/article/details/107465877