01背包的倍数或余数相关的问题,P2946 [USACO09MAR]Cow Frisbee Team S

P2946 [USACO09MAR] Cow Frisbee Team S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述

老唐最近迷上了飞盘,约翰想和他一起玩,于是打算从他家的 N 头奶牛中选出一支队伍。

每只奶牛的能力为整数,第 i 头奶牛的能力为Ri​ 。飞盘队的队员数量不能少于 11、大于N。一支队伍的总能力就是所有队员能力的总和。

约翰比较迷信,他的幸运数字是 F ,所以他要求队伍的总能力必须是 F 的倍数。请帮他

算一下,符合这个要求的队伍组合有多少?由于这个数字很大,只要输出答案对 108108 取模的值。

输入格式

第一行:两个用空格分开的整数:N 和 F。

第二行到N+1 行:第i+1 行有一个整数Ri​ ,表示第 i 头奶牛的能力。

输出格式

第一行:单个整数,表示方案数对 108108 取模的值。

输入输出样例

输入 #1复制

4 5 
1 
2 
8 
2 

输出 #1复制

3 

说明/提示

数据范围与约定
  • 对于 100%100% 的数据,11≤N≤2000,1≤F≤1000 ,1≤Ri​≤105 。

题目中给的数据范围:对于 100% 的数据,1≤N≤2000,1≤F≤1000 ,1≤Ri≤1e5;
如果按照模板写这道题就是:有N中物品,最大的重量为N*Ri,分配一个数组f[N][N*Ri],
数组f表示前i个物品重量为j的方案数
将说有的重量类型算出来,在从中找出F的倍数,累加即可得到结果;
但这显然是行不通的,因为我们无法分配这么大的数组

所以我们找出的道题的性质,然后加以利用:

性质1:这道题目要求寻找出能组合成F的倍数的方案数,而(a+b)%F==(a%F+b%F)%F;

因此我们就可以将f[N][N*Ri]改写成f[N][F],
横坐标表示%F的余数,f的表示前i头奶牛的能力值和%F==j的方案有几种;

则状态转移方程为:

f[i][j]+=(f[i-1][j]+f[i-1][(j-cow[i]+F)%F])%1e8;

这里有两个需要注意的地方

注1:f[i-1][(j-cow[i]+F)%F]中(j-cow[i]+F)是有可能前一个状态在j的右边,即j-cow[i]可能小于零;
注2:初始化的时候所有f[i][arr[i]]=1;


#include<iostream>
#include<set>
#include<vector>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<map>
#include<math.h>


using namespace std;
typedef long long LL;
const int N = 1e4;
const int mod = 1e8;
int f[N][N], arr[N];
int n, m;

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &arr[i]);
		arr[i] %= m;
	}

	for (int i = 1; i <= n; i++) {
		f[i][arr[i]] = 1;
	}

	for (int i = 1; i <= n; i++) {
		for (int j = 0; j < m; j++) {
			f[i][j] += (f[i - 1][j] + f[i - 1][(j - arr[i] + m) % m]) % mod;
		}
	}

	/*for (int i = 1; i <= n; i++) {
		for (int j = 0; j <= m; j++) {
			cout << f[i][j] << " ";
		}
		cout << endl;
	}*/

	printf("%d\n", f[n][0]);
	return 0;
}

posted @ 2023-06-24 17:19  Landnig_on_Mars  阅读(4)  评论(0编辑  收藏  举报  来源