2017 ICPC North American Qualifier Contest - Canonical Coin Systems

Canonical Coin Systems

https://open.kattis.com/problems/canonical

题目描述

A coin system S{1,5,10,25,100,200}11200200 2S, we assume that there is an unlimited supply of coins of each denomination, and we also assume that S1S

Cashiers all over the world face (and solve) the following problem: For a given coin system and a positive integer amount owed to a customer, what is the smallest number of coins required to dispense exactly that amount? For example, suppose a cashier in Canada owes a customer 8325+25+10+10+10+1+1+1825+25+25+5+1+1+17

Your challenge is this: Given a coin system S={c1,c2,…,cn}SSxx{1,3,4}, for which 64+1+133+32S

Input

Input consists of a single case. The first line contains an integer n (2≤n≤100)nc1 c2 … cnc1=1c1<c2<…<cn≤106

Output

Output “canonical” if the coin system is canonical, or “non-canonical” if the coin system is non-canonical.

Sample Input 1
4
1 2 4 8
Sample Output 1
canonical
Sample Input 2
3
1 5 8
** Sample Output 2**
non-canonical
Sample Input 3
6
1 5 10 25 100 200
Sample Output 3
canonical

解析

问一种货币体系,对于任意数量的money,按照贪心和dp完全背包的做法拼凑,用的纸币数量是否一致。

思路:
一遍dp加一遍贪心
分开跑超时,要写一块,就对背包和贪心分别变形.
这里引用背包九讲的一句话

扫描二维码关注公众号,回复: 8656651 查看本文章

为什么这个算法就可行呢?首先想想为什么01背包中要按照v递减的次序来
循环。让v递减是为了保证第i次循环中的状态F[i, v]是由状态F[i − 1, v − Ci]递
推而来。换句话说,这正是为了保证每件物品只选一次,保证在考虑“选入
第i件物品”这件策略时,依据的是一个绝无已经选入第i件物品的子结果F[i −
1, v − Ci]。而现在完全背包的特点恰是每种物品可选无限件,所以在考虑“加
选一件第i种物品”这种策略时,却正需要一个可能已选入第i种物品的子结
果F[i, v − Ci],所以就可以并且必须采用v递增的顺序循环。这就是这个简单的
程序为何成立的道理。
值得一提的是,上面的伪代码中两层for循环的次序可以颠倒。这个结论有
可能会带来算法时间常数上的优化。

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <cstring>
const int INF = 0x3f3f3f3f;
const int MAX = 2 * 1000000 + 10;
using namespace std;

int a[110], dp[MAX], res[MAX];
void read(int &x)
{
	x = 0; int  f = 1; char c = getchar();
	while (c<'0'&&c>'9') {
		if (c == '-')	f = -1;
		c = getchar();
	}
	while (c >= '0'&&c <= '9') {
		x = x * 10 + c - '0';
		c = getchar();
	}
}
int main()
{
	int n;
	read(n);
	for (int i = 0; i < n; i++)
		read(a[i]);
	int maxx = 2 * a[n - 1] + 10;
	dp[0] = 0;
	for (int i = 1; i <= maxx; i++)	dp[i] = INF;
	for (int j = 0; j <= maxx; j++) {		//容量
		for (int i = 0; i < n; i++)			//种类
			if (j >= a[i]) {
				dp[j] = min(dp[j], dp[j - a[i]] + 1);	//另类版完全背包dp
				res[j] = res[j - a[i]] + 1;				//贪心版dp
			}
		if (dp[j] != res[j]) {
			cout << "non-canonical" << endl;
			return 0;
		}
	}
	cout << "canonical" << endl;
	return 0;
}
发布了62 篇原创文章 · 获赞 13 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/UnKfrozen/article/details/89280714