题意:有四种硬币,1分,5分,10分,25分,分别有a,b,c,d种,给出一个n分钱,要求你求出组成n分钱最多需要的硬币数量,并且输出组成它的各种硬币的数量......

学到的东西:这个题目我是用两种方法做的,一个是完全背包,一个是多重背包。做完这个题目,我对背包的理解可以说上了个层次......还有记录路径的方法,反过来求出各个硬币的数量,都是我以前做的题目没有涉及到的.......

要求出各个硬币有多少种,只需要记录路径,在开一个数组统计p-path[p],为什么可以如此?很容易想到path[p]=p-v[i]

如此,p-path[p]==p-(p-v[i])==v[i],而v[i]正好是硬币的价值........这道题目令我思考到一个东西,一般的背包问题的初始值都是将dp全部赋值为0的,而在这边dp[0]==1,并且在动态转移的过程中,还限制了dp[j]>0才可以转移,与背包的模板的动态转移不同啊?为什么要这样呢?

在一般的dp题目里面,有体积,价值,所要求的不是最大价值就是最小价值,而定义的dp[i][j]意义是装有i件物品,体积为j的最大值为dp[i][j],简化为一维的dp[j]代表的是在体积为j的时候最大价值为dp[j]。仔细观察,可以发现,在dp[j-v[i]]时,dp[j]本身就可以从dp[j-v[i]]那边得到值,因为dp[j-v[i]]这个地方,它可以组成dp[j]......也就是说,它不需要去判断在dp[j-v[i]]前面是否有数可以组成dp[j-v[i]],因此dp[j-v[i]]为不为0,不影响最终结果......

而这个题目却不同,需要赋值dp[0]=1;在动态转移的时候还得保证dp[j-v[i]]>0,这是因为题意是要求组成n分钱需要的最多硬币数量,那么你要可以组成dp[n],dp[n-v[i]]必须要可以组成,否则就会出错.......同理,这道题目原理如此,在做给出n分钱,每种钱币有a,b,c,.....种,求组成n分钱最多的种数,也是要赋值dp[0]=1的,原理是一样的........

这告诫我,在做dp题目时,要仔细思考好其前后的关系,以及中间推导至最后的关系.....

完全背包代码:

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int dp[10010],ans[10010],num[10010],path[10010];
int p,c[5]={0,1,5,10,25},t[5];
int main()
{
while(scanf("%d %d %d %d %d",&p,&t[1],&t[2],&t[3],&t[4])>0&&(p+t[1]+t[2]+t[3]+t[4]))
{
memset(dp,0,sizeof(dp));
memset(ans,0,sizeof(ans));
memset(path,0,sizeof(path));
dp[0]=1;
for(int i=1;i<=4;i++)
{
memset(num,0,sizeof(num));
for(int j=c[i];j<=p;j++)
if(dp[j-c[i]]&&dp[j-c[i]]+1>dp[j]&&num[j-c[i]]<t[i]) //一般来说,完全背包的硬币是没有限制的,后一个数必然可以由前面的某个数组成,所以也就不需要dp[j-c[i]]>0,
但是,这次用到的完全背包其硬币数受到了限制,也就导致有些数根本不可能组成,所以要把这些数排除
{
dp[j]=dp[j-c[i]]+1;
num[j]=num[j-c[i]]+1;
path[j]=j-c[i];
}
}
int i=p;
if(dp[p]>0)
{
while(i!=0)
{
ans[i-path[i]]++;
i=path[i];
}
printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",ans[1],ans[5],ans[10],ans[25]);
}
else printf("Charlie cannot buy coffee.\n");
}
return 0;
}
 多重背包代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
struct node
{
int num;
int cout;
int dw;
}w[20001];
int dp[20001],c[5]={0,1,5,10,25},t[5],path[20001][2],ans[20001];
int p;
int main()
{
while(scanf("%d%d%d%d%d",&p,&t[1],&t[2],&t[3],&t[4])>0&&(p+t[1]+t[2]+t[3]+t[4]))
{
int cnt=0;
for(int i=1;i<=4;i++)
{
int k=1;
while(t[i]-k>0)
{
w[cnt].num=k*c[i];
w[cnt].cout=k;
w[cnt++].dw=c[i];
t[i]-=k;
//if(i==1)
//printf("%d\n",w[cnt-1].dw);
k*=2;
} w[cnt].num=t[i]*c[i];
w[cnt].cout=t[i];
w[cnt++].dw=c[i];
//if(i==1)
// printf("%d\n",w[cnt-1].dw);
}
memset(dp,0,sizeof(dp));
memset(path,0,sizeof(path));
memset(ans,0,sizeof(ans));
dp[0]=1;
for(int i=0;i<cnt;i++)
{
for(int j=p;j>=w[i].num;j--)
if(dp[j-w[i].num]>0&&dp[j]<dp[j-w[i].num]+w[i].cout) //多重背包的二进制拆分其原理就是转化成01背包来处理,而01背包是从n推导至0,在i==1时,dp[n-w[i].num]+w[i].cout>dp[n]除非w[i].cout==0,否则就是必然的,但是同时,你要可以组成dp[n],那么你首先要可以组成dp[n-w[i].num],那么也就是说dp[n-w[i].num]必须要>0,才可以进行动态转移
{
dp[j]=dp[j-w[i].num]+w[i].cout;
//printf("%d %d %d %d\n",w[i].num,w[i].dw,w[i].cout,dp[j]);
path[j][0]=j-w[i].num;
path[j][1]=i;
}
}
//printf("%d\n",dp[p]);
if(dp[p]!=0)
{
while(p!=0)
{
int tmp=p-path[p][0];
int j=path[p][1];
ans[w[j].dw]+=w[j].cout;
p=path[p][0];
}
printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",ans[1],ans[5],ans[10],ans[25]);
}
else printf("Charlie cannot buy coffee.\n");
}
return 0;
}
 

dp之完全背包poj1787(完全背包以及路径记录 推荐)的更多相关文章

  1. ZOJ 3812 We Need Medicine(dp、背包、状态压缩、路径记录)

    参考:http://blog.csdn.net/qian99/article/details/39138329 参考的链接里说明得很好,注释也很好...thanks for sharing 朴素的想法 ...

  2. HDOJ(HDU).2159 FATE (DP 带个数限制的完全背包)

    HDOJ(HDU).2159 FATE (DP 带个数限制的完全背包) 题意分析 与普通的完全背包大同小异,区别就在于多了一个个数限制,那么在普通的完全背包的基础上,增加一维,表示个数.同时for循环 ...

  3. 01背包与完全背包(dp复习)

    01背包和完全背包都是dp入门的经典,我的dp学的十分的水,借此更新博客的机会回顾一下 01背包:给定总容量为maxv的背包,有n件物品,第i件物品的的体积为w[i],价值为v[i],问如何选取才能是 ...

  4. Charlie's Change_完全背包&&路径记录

    Description Charlie is a driver of Advanced Cargo Movement, Ltd. Charlie drives a lot and so he ofte ...

  5. HDU2159--二维费用背包,三重背包

    FATE Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submi ...

  6. 背包问题(01背包,完全背包,多重背包(朴素算法&&二进制优化))

    写在前面:我是一只蒟蒻~~~ 今天我们要讲讲动态规划中~~最最最最最~~~~简单~~的背包问题 1. 首先,我们先介绍一下  01背包 大家先看一下这道01背包的问题  题目  有m件物品和一个容量为 ...

  7. poj1742(多重背包分解+01背包二进制优化)

    Description People in Silverland use coins.They have coins of value A1,A2,A3...An Silverland dollar. ...

  8. 浙大PAT CCCC L3-001 凑零钱 ( 0/1背包 && 路径记录 )

    题目链接 分析 : 就是一个 0/1 背包,但是需要记录具体状态的转移情况 这个可以想象成一个状态转移图,然后实际就是记录路径 将状态看成点然后转移看成边,最后输出字典序最小的路径 这里有一个很巧妙的 ...

  9. poj 2184(dp变形,进一步加深01背包)

    点击打开链接 题意: 给你n个物品,每个物品都有两个属性,s和f,要求选择一些物品,使sum(s)+sum(f)最大,并且sum(s)>=0&&sum(f)>=0, 根据0 ...

随机推荐

  1. java学习笔记1--开发环境平台总结

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note1.html,转载请注明源地址. 1.Java程序设计语言 Java程序设计语言是一种高 ...

  2. Vue组件开发实践之scopedSlot的传递

    收录待用,修改转载已取得腾讯云授权 导语 现今的前端开发都讲究模块化组件化,即把公共的交互和功能封装到一个个的组件之中,在开发整体界面的时候就能像搭积木一样快速清晰高效.在使用Vue开发我们的vhtm ...

  3. Spark学习散点总结

    使用Spark 时,通常会有两种模式.一.在交互式编程环境(REPL, a.k.a spark-shell)下实现一些代码,测试一些功能点.二.像MapReduce 那样提前编写好源代码并编译打包(仅 ...

  4. EasyUI-EasyUI框架入门学习

    前言 新项目的开发前端技术打算采用EasyUI框架(基于EasyUI较为丰富的UI组件库),项目组长将前端EasyUI这块的任务分配给了我.在进行开发之前,需要我这菜鸟对EasyUI框架进行一些基础的 ...

  5. Cognos清除本地高速缓存的利与弊

    场景:在开发报表初期,往往我们遇到过这种问题,我们手工修改了DB中的测试数据,但是返回报表看,数据还没有更新,难道是设计出问题了?NO,不要慌,这是因为Cognos为了查询效率设计了高速缓存的选项. ...

  6. Oracle DB 复制数据库

    • 列出创建副本数据库的目的 • 选择用于复制数据库的方法 • 使用RMAN 复制数据库 • 使用RMAN 备份复制数据库 • 基于正在运行的实例复制数据库 使用副本数据库 • 使用副本数据库可执行以 ...

  7. jtemplate 为javascript前端html模版引擎

    最近的项目中用到了jtemplate, 它是客户端基于javascript的模板引擎,绑定的数据为json对象.以前我在页面上显示数据列表时最喜欢用Repeater控件了,因为它相对与其它几个服务端控 ...

  8. Android Bluetooth模块学习笔记

    一.蓝牙基础知识 1.蓝牙( Bluetooth )是一种无线技术标准,可实现固定设备.移动设备和楼宇个人域网之间的短距离数据交换.蓝牙基于设备低成本的收发器芯片,传输距离近.低功耗. 2.微波频段: ...

  9. DBMS_SQL使用

    一.简介 DBMS_SQL包提供一个接口,用于执行动态SQL(包括DDL 和DML). DBMS_SQL定义了一个实体叫游标ID,游标ID是一个PL/SQL整型数,通过游标ID,可以对游标进行操作. ...

  10. idea 更换编辑器背景图片

    插件名称是:BackgroundImage, 安装后效果图