前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ACM刷题之路(二十四)HDU 2844 多重背包转换 Coins

ACM刷题之路(二十四)HDU 2844 多重背包转换 Coins

作者头像
Designer 小郑
发布2023-08-01 08:15:37
1550
发布2023-08-01 08:15:37
举报
文章被收录于专栏:跟着小郑学JAVA跟着小郑学JAVA

题目链接:传送门

Coins

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 24496    Accepted Submission(s): 8740

Problem Description

Whuacmers use coins.They have coins of value A1,A2,A3...An Silverland dollar. One day Hibix opened purse and found there were some coins. He decided to buy a very nice watch in a nearby shop. He wanted to pay the exact price(without change) and he known the price would not more than m.But he didn't know the exact price of the watch. You are to write a program which reads n,m,A1,A2,A3...An and C1,C2,C3...Cn corresponding to the number of Tony's coins of value A1,A2,A3...An then calculate how many prices(form 1 to m) Tony can pay use these coins.

题意:

3 10  // N=3 三个硬币 M = 10 所求最大方案价值为10

1 2 4 //三个硬币的价值分别为1 、 2 、4

2 1 1//三个硬币的数量分别为2 、1 、 1

求 能否满足用这三种硬币 凑出价值1 到 10 的方案 

比如1 = 1;2 = 1 + 1;3 = 2 + 1;4 = 4;5 = 4 + 1;6 = 4 + 2;7 = 4 + 2 + 1;8 = 4 + 2 + 1 + 1;

所以1到10可以满足8个币值的方案,输出8.

分析:

其实这题也看似母函数的模板题,但这周再集训学dp动态规划,所以我用多重背包来AC。

我首先想到的思路是多重背包,转01背包,加一个二进制优化,就是下列代码所示:

代码语言:javascript
复制
        for (int i = 0; i < n; i++) {
            int bei = 1;
            while (a[i].geshu > 0) {
                if (a[i].geshu & 1) {
                    b[len++] = a[i].jiazhi * bei;
                }
                bei *= 2;
                a[i].geshu /= 2;
            }
        }

后来就一直超时,加了输入挂还是超时,所以试了一种多重背包转换为二进制01背包+完全背包的方法。

即:如果你某个硬币的数量乘以币值,大于等于要求的M(第一行第二个数),那么就可以看作是完全背包来做题了,套完全背包的模板:(有点断章取义,可以从最后的总代码来看)

代码语言:javascript
复制
if (jia[i] * shu[i] >= m)
{
	for (int j = jia[i]; j <= m; j++) 
        {
    	    dp[j] = maxx(dp[j], dp[j - jia[i]] + jia[i]);
	}
}

如果某个硬币的数量乘以币值,不到要求的M,那么就需要转化为2进制01背包了。

代码如下所示:

代码语言:javascript
复制
else 
{
    for (int k = 1; k  <= shu[i]; k *= 2)
    {
	for (int j = m; j >= jia[i] * k; j--) 
        {
		dp[j] = maxx(dp[j], dp[j - jia[i] * k] + jia[i] * k);
	}
	shu[i] -= k;
    }
	if (shu[i] > 0)
        {
		for (int j = m; j >= shu[i] * jia[i]; j--) 
                {
			dp[j] = maxx(dp[j], dp[j - shu[i] * jia[i]] + shu[i] * jia[i]);
	        }
	}
}

刚开始初始化dp数组为负无穷,且dp[0] = 1;

如果dp[i]可以为正数,那么肯定是从dp[0]加硬币加上来的,所以就可以满足刚好凑到i价值的情况。

代码语言:javascript
复制
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int maxx(int x, int y) {
	return x > y ? x : y;
}
int jia[102];
int shu[102];
int dp[100010];
int main()
{
	int n, m;
	while (~scanf_s("%d%d", &n, &m)) {
		if (n == 0 && m == 0) break;
		for (int i = 0; i < n; i++) {
			scanf_s("%d", &jia[i]);
		}
		for (int i = 0; i < n; i++) {
			scanf_s("%d", &shu[i]);
		}
		fill(dp, dp + 100010, -999999);
		dp[0] = 1;
		for (int i = 0; i < n; i++) {
			if (jia[i] * shu[i] >= m) {
				for (int j = jia[i]; j <= m; j++) {
					dp[j] = maxx(dp[j], dp[j - jia[i]] + jia[i]);
				}
			}
			else {
				for (int k = 1; k  <= shu[i]; k *= 2) {
					for (int j = m; j >= jia[i] * k; j--) {
						dp[j] = maxx(dp[j], dp[j - jia[i] * k] + jia[i] * k);
					}
					shu[i] -= k;
				}
				if (shu[i] > 0) {
					for (int j = m; j >= shu[i] * jia[i]; j--) {
						dp[j] = maxx(dp[j], dp[j - shu[i] * jia[i]] + shu[i] * jia[i]);
					}
				}
			}
		}
		int ans = 0;
		for (int i = 1; i <= m; i++) {
			if (dp[i]) {
				ans++;
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-07-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Coins
    • 题意:
    • 分析:
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档