【CQOI2008】矩阵的个数题解

本文深入探讨了一道涉及多列总和的数据结构与算法问题,通过动态规划(DP)方法解决。文章详细解释了如何设定状态f[i][j][k],并给出了具体的状态转移方程,避免了不必要的前缀和操作,提供了完整的C++代码实现。

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]);
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值