硬币问题——固定终点的最长路和最短路

问题描述:

有n种硬币,面值分别为V1,V2...,Vn,每种都有无限多。给定非负整数S,可以选用多少个硬币,使得面值之和恰好为S?输出硬币数目的最小值和最大值。0 <= n <= 100, 0 <= S <= 10000, 1 <= Vi <= S。

分析:

本题的本质还是DAG上的路径问题。我们把每种面值看作一个点,表示"还需要凑足的面值",则初始状态为S,目标状态为0。若当前的状态i,每使用一个硬币j,状态便转移到i-Vj。这个模型和嵌套矩形一题类似,但也有些明显的不同之处:嵌套矩形中并没有确定路径的起点和终点(可以把任意矩形放在第一个和最后一个),而本题的起点必须是S,终点必须是0。把终点固定之后"最短路"才是有意义的。在嵌套矩形中,最短序列显然是空(如果不允许空的话,就是单个矩形,不管怎样都是平凡的),而本题的最短路径却不是那么容易确定的。

接下来考虑"硬币问题"。注意到最长路和最短路的求法是类似的,下面只考虑最长路。由于终点固定,d(i)的确切含义变为"从节点i出发到节点0的最长路径长度"。

下面是求最长路的代码(错误的):

int dp(int S)//错误
{
	int& ans=d[S];
	if(ans>=0) return ans;
	ans=0;
	for(int i=1;i<=n;i++)if(S>=V[i])
		ans=max(ans,dp(S-V[i])+1);

	return ans;
}
/*此代码有一个致命的错误,即由于节点S不一定真的能到达节点0,所以需要用特殊的d[S]值表
示"无法到达",但在上述代码中,如果S根本无法继续往前走,返回值是0,将被误以为是"不能
走已经到达终点"的意思。*/

正确的解法一:

int dp(int S)
{
	int& ans=d[S];
	if(ans != -1) return ans;
	ans=-1<<30;
	for(int i=1;i<=n;i++)if(S>=V[i])
		ans=max(ans,dp(s-V[i])+1);

	return ans;
}

注意:在记忆化搜索中,如果用特殊值表示"还没有算过",则必须将其和其他特殊值(如无解)区分开。

正确的解法二:

int dp(int S)
{
	if(vis[S]) return d[S];
	vis[S]=1;
	int& ans=d[S];
	ans=-1<<30;
	for(int i=1;i<=n;i++)if(S>=V[i]){
		ans=max(ans,dp(S-V[i])+1);
	}
	return ans;
}

尽管多了一个数组,但可读性增强了许多:再也不用担心特殊值之间的冲突了,在任何情况下,记忆化搜索的初始化都可以用memset(vis,0,sizeof(vis))执行。

本题要求最小、最大两个值,记忆化搜索就必须写两个。在这种情况下,还是递推来得更加方便(此时需注意递推的顺序):

min[0]=max[0]=0;
for(int i=1;i<=n;i++)
{
    min[i]=INF; max[i]=-INF;
}
for(int i=1;i<n;i++)
   for(int j=1;j<=v;j++)
         if(i>=V[j])
      {
          min[i]=min(min[i],min[i-V[j]]+1);
          max[i]=max(max[i],max[i-V[j]]+1);
         }
printf("%d %d\n",min[S],max[S]);

完整代码:

#include "stdio.h"
#define INF 1<<30
#define maxn 100+10
int V[maxn],n;
int min[maxn],max[maxn];

inline int Min(int a,int b){return a<b?a:b;}
inline int Max(int a,int b){return a>b?a:b;}

//打印可行的方案
void print_ans(int* d,int S){
	for(int i=1;i<=n;i++){
		if(S>=V[i] && d[S]==d[S-V[i]]+1){
			printf("%d ",V[i]);
			print_ans(d,S-V[i]);
			break;
		}
	}
}

int main()
{
	int S;
	//输入面值S和面值的种数n
	while(~scanf("%d%d",&S,&n))
	{
		for(int i=1;i<=n;i++){
			scanf("%d",&V[i]);
		}

		max[0]=0; min[0]=0;
		for(int i=1;i<=S;i++)
		{
			min[i]=INF; max[i]=-INF;
		}

		//递推实现
		for(int i=1;i<=S;i++){
			for(int j=1;j<=n;j++){
				if(i>=V[j]){
					min[i]=Min(min[i],min[i-V[j]]+1);
					max[i]=Max(max[i],max[i-V[j]]+1);
				}
			}
		}

		print_ans(min,S);	printf("    min\n");
		print_ans(max,S);	printf("    max\n");
		printf("min:%d max:%d\n",min[S],max[S]);
	}

	return 0;
}

硬币问题——固定终点的最长路和最短路,布布扣,bubuko.com

时间: 2024-10-27 10:43:47

硬币问题——固定终点的最长路和最短路的相关文章

聪明的kk(南阳oj171)(dp固定终点的最长路)

聪明的kk 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描述 聪明的"KK" 非洲某国展馆的设计灵感源于富有传奇色彩的沙漠中陡然起伏的沙丘,体现出本国不断变换和绚丽多彩的自然风光与城市风貌.展馆由五部分组成,馆内影院播放名为<一眨眼的瞬间>的宽银幕短片,反映了建国以来人民生活水平和城市居住环境的惊人巨变. 可移动"沙丘"变戏法 的灵感源于其独特而雄伟的自然景观--富于传奇色彩的险峻沙丘.宏伟的结构.可循环的建材,与大自然相得益

寒冰王座(DGA最长路/完全背包)

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 12584    Accepted Submission(s): 6379 Problem Description 不死族的巫妖王发工资拉,死亡骑士拿到一张N元的钞票(记住,只有一张钞票),为了防止自己在战斗中频繁的死掉,他决定给自己买一些道具,于是他来到了地精商店前. 死亡骑士:"我要买道

ZOJ3088 Easter Holidays spfa 最长路 最短路 路径打印

题目链接: 3088 题意:一个滑雪胜地包含了n 个地点,m 个滑雪斜坡,k 架雪橇,其中2≤n≤1000.1≤m≤1000.1≤k≤1000.滑雪斜坡和雪橇总是从一个地点到另一个地点:滑雪斜坡是从高地点到低地点,而雪橇刚好相反(注意,雪橇不能下降).Per 是一个滑雪初学者,他很害怕雪橇,尽管他想滑得尽可能快.现在,他发现他可以选择不同的雪橇和滑雪斜坡.他现在想这样安排他的滑雪行程: 1) 从一架雪橇的起点出发并最终回到起点. 2) 这个过程分为两个阶段:第一阶段,乘坐一架或多架雪橇上升:第二

【HDOJ1217】【Floyd求最长路】

http://acm.hdu.edu.cn/showproblem.php?pid=1217 Arbitrage Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 9455    Accepted Submission(s): 4359 Problem Description Arbitrage is the use of discrepa

UVA 10029 Edit Step Ladders ——(DAG求最长路)

题意:升序的给出一本若干个单词,每个单词都可删除一个字母,添加一个字母或者改变一个字母,如果任意一个操作以后能变成另外一个字典中的单词,那么就连一条有向边,求最长的长度. 分析:DAG的最长路和最短路在算法竞赛入门里边原原本本有的,结果我现在忘记了,,真是太弱了..方法就是,用map对应键值(以建图),然后删除操作和修改操作可以看做同一个操作,之后每个操作都是在相应的位置添加一个 '*' 就可以了.想说的有两点,一个是为什么删除和修改可以看做一个操作,其实删除这个操作根本就是多余的,因为一个单词

SDUTOJ 2498 AOE网上的关键路径(最长路)

AOE网上的关键路径 Time Limit: 1000MS Memory limit: 65536K 题目描述 一个无环的有向图称为无环图(Directed Acyclic Graph),简称DAG图. AOE(Activity On Edge)网:顾名思义,用边表示活动的网,当然它也是DAG.与AOV不同,活动都表示在了边上,如下图所示: 如上所示,共有11项活动(11条边),9个事件(9个顶点).整个工程只有一个开始点和一个完成点.即只有一个入度为零的点(源点)和只有一个出度为零的点(汇点)

HDU - 6201 transaction transaction transaction(spfa求最长路)

题意:有n个点,n-1条边的无向图,已知每个点书的售价,以及在边上行走的路费,问任选两个点作为起点和终点,能获得的最大利益是多少. 分析: 1.从某个结点出发,首先需要在该结点a花费price[a]买书,然后再在边上行走,到达目的地后,在目的地b获得price[b]. 2.因此可以建立两个虚拟结点, 虚拟结点1连向n个点,边权分别为-price[i],表示以i为起点,需花费price[i]买书. n个点连向虚拟结点2,边权分别为price[i],表示以i为终点,通过卖书可得price[i]. 3

树径问题 最长路问题。。

先看看理论: 假设 s-t这条路径为树的直径,或者称为树上的最长路 现有结论,从任意一点u出发搜到的最远的点一定是s.t中的一点,然后在从这个最远点开始搜,就可以搜到另一个最长路的端点,即用两遍广搜就可以找出树的最长路 证明: 1    设u为s-t路径上的一点,结论显然成立,否则设搜到的最远点为T则 dis(u,T) >dis(u,s)     且  dis(u,T)>dis(u,t)   则最长路不是s-t了,与假设矛盾 2   设u不为s-t路径上的点 首先明确,假如u走到了s-t路径上

HDU 6201 transaction transaction transaction(拆点最长路)

transaction transaction transaction Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Others) Total Submission(s): 88    Accepted Submission(s): 39 Problem Description Kelukin is a businessman. Every day, he travels around