10616 - Divisible Group Sums(dp背包)

本文探讨如何通过动态规划解决一个特定的组合数学问题,即从一组数字中选择若干个数字,使它们的和能够被指定的整数整除。详细介绍了输入输出格式、解题思路及代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem H

Divisible Group Sums

Input: Standard Input

Output: Standard Output

Time Limit: 1 Second

 

Given a list of N numbers you will be allowed to choose any M of them. So you can choose in NCM ways. You will have to determine how many of these chosen groups have a sum, which is divisible by D.

 

Input

The input file contains maximum ten sets of inputs. The description of each set is given below.

 

The first line of each set contains two integers N (0<N<=200) and Q (0<Q<=10). Here N indicates how many numbers are there and Q is the total no of query. Each of the next N lines contains one 32 bit signed integer. Our queries will have to be answered based on these Nnumbers. Next Q lines contain queries. Each query contains two integers D (0<D<=20) and M (0<M<=10) whose meanings are explained in the first paragraph.

 

Input is terminated by a case whose N=0 and Q=0. This case should not be processed.

 

Output

For each set of input, print the set number. Then for each query in the set print the query number followed by the number of desired groups. See sample output to know the exact output format.

 

Sample Input                             Output for Sample Input

10 2
1
2
3
4
5
6
7
8
9
10
5 1
5 2
5 1
2
3
4
5
6
6 2
0 0

SET 1:

QUERY 1: 2

QUERY 2: 9

SET 2:

QUERY 1: 1

题意:给定n个数,从中选出m个(不重复)问能组合出整除d的和有几种。

思路:状态很明显,dp[i][j].i表示用i个数字,j表示总和,如果直接开数组看起来开不下,但是d最多20,对每个数字先取mod d。这样每个数字最多就20,那么总和最多20 * 200,这样就可以了。数字不能重复,要用逆推。有点类似01背包。只不过存放的是总数,并且多一维来保存放几个数,

代码:

#include <stdio.h>
#include <string.h>
const int N = 15;
const int MAXN = 205;

int n, q, d, m, sum, num[MAXN], a[MAXN];
long long dp[N][MAXN * 20];

long long solve() {
    long long ans = 0;
    memset(dp, 0, sizeof(dp));
    dp[0][0] = 1;
    for (int k = 0; k < n; k ++)
	for (int i = m; i > 0; i --)
	    for (int j = sum; j >= a[k]; j --)
		dp[i][j] += dp[i - 1][j - a[k]];
    for (int i = 0; i <= sum; i += d)
	ans += dp[m][i];
    return ans;
}

int main() {
    int tt = 1;
    while (~scanf("%d%d", &n, &q) && n + q) {
	for (int i = 0; i < n; i ++)
	    scanf("%d", &num[i]);
	printf("SET %d:\n", tt ++);
	int ttt = 1;
	while (q --) {
	    sum = 0;
	    scanf("%d%d", &d, &m);
	    for (int i = 0; i < n; i ++) {
		a[i] = (num[i] % d + d) % d;
		sum += a[i];
	    }
	    printf("QUERY %d: %lld\n", ttt ++, solve());
	}
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值