POJ1276 多重背包DP题

这道题弄了一个早上…一看题目是多重背包题直接敲了一个三重for循环,无限TLE,下面附上好几种解法。

Description

A Bank plans to install a machine for cash withdrawal. The machine is able to deliver appropriate @ bills for a requested cash amount. The machine uses exactly N distinct bill denominations, say Dk, k=1,N, and for each denomination Dk the machine has a supply of nk bills. For example,

N=3, n1=10, D1=100, n2=4, D2=50, n3=5, D3=10

means the machine has a supply of 10 bills of @100 each, 4 bills of @50 each, and 5 bills of @10 each.

Call cash the requested amount of cash the machine should deliver and write a program that computes the maximum amount of cash less than or equal to cash that can be effectively delivered according to the available bill supply of the machine.

Notes:
@ is the symbol of the currency delivered by the machine. For instance, @ may stand for dollar, euro, pound etc.

Input

The program input is from standard input. Each data set in the input stands for a particular transaction and has the format:

cash N n1 D1 n2 D2 … nN DN

where 0 <= cash <= 100000 is the amount of cash requested, 0 <=N <= 10 is the number of bill denominations and 0 <= nk <= 1000 is the number of available bills for the Dk denomination, 1 <= Dk <= 1000, k=1,N. White spaces can occur freely between the numbers in the input. The input data are correct.

Output

For each set of data the program prints the result to the standard output on a separate line as shown in the examples below.

Sample Input

735 3 4 125 6 5 3 350
633 4 500 30 6 100 1 5 0 1
735 0
0 3 10 100 10 50 10 10

Sample Output

735
630
0
0

TLE代码

直接暴力三重循环,注定超时,吸取教训...
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;int dp[1000005];int main()
{int count;while (scanf("%d",&count)!=EOF){int n;scanf("%d",&n);int num[1005];int coin[11];memset(num,0,sizeof(num));memset(coin,0,sizeof(coin));for (int i = 1;i<=n;i++)scanf("%d%d",&num[i],&coin[i]);for (int i = 1;i<=n;i++)for (int j = 1;j<=num[i];j++)for (int k = count;k>=coin[i]*j;k--)dp[k] = max(dp[k],dp[k-j*coin[i]]+j*coin[i]);printf("%d\n",dp[count]);memset(dp,0,sizeof(dp));}
}

1.解法一

这题的硬币种类最多只有10个,其实太小了,直接遍历这些硬币,dp[i]表示能不能组成i块钱,能就dp[i]=1,否则为0,最后向下枚举cash即可
跑了579MS
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;int dp[1000005];int main()
{int count;while (scanf("%d",&count)!=EOF){int n;scanf("%d",&n);int num[20];int coin[11];memset(dp,0,sizeof(dp));memset(num,0,sizeof(num));memset(coin,0,sizeof(coin));for (int i = 1;i<=n;i++)scanf("%d%d",&num[i],&coin[i]);dp[0] = 1;for (int i = 1;i<=n;i++)for (int j = count;j>=0;j--){if (dp[j])for (int k = 1;k<=num[i];k++)if (j+k*coin[i] <=count)dp[j+k*coin[i]]=1;}for (int i = count;i>=0;i--)if (dp[i]){printf("%d\n",i);break;}
}
}

解法二

将多重背包化作完全背包,加个count计数…跑了210MS

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[1000001];
int countn[1000001];
int main()
{int cash,num,i,j;int n[15];int w[15];while(cin>>cash>>num){for(i=0;i<num;i++)cin>>n[i]>>w[i];memset(dp,0,sizeof(dp));for(i=0;i<num;i++){memset(countn,0,sizeof(countn));for(j=w[i];j<=cash;j++)if(dp[j]<dp[j-w[i]]+w[i]&&countn[j-w[i]]<n[i]){dp[j]=dp[j-w[i]]+w[i];countn[j]=countn[j-w[i]]+1;}}cout<<dp[cash]<<endl;}return 0;
}

解法三

将多重背包用二进制思想分解成01背包
跑了110MS

#include<stdio.h>
#include<cmath>
#include<string.h>
#include<iostream>
using namespace std;
int cash,n,v[10100],dp[1000001];//数组要开的足够大
int main()
{while(scanf("%d",&cash) != EOF){memset(dp,0,sizeof(dp));memset(v,0,sizeof(v));scanf("%d",&n);int cnt = 0;for(int i = 1; i <= n ; i ++){int a,b,t = 1;scanf("%d%d",&b,&a);if(b != 0){while(t < b)//此处是把b按照二进制分开{b = b - t;v[cnt++] = a * t;t *= 2;}v[cnt++] = b*a;}}if(n == 0 &&cash == 0){printf("0\n");continue;}for (int i = 0;i<cnt;i++)for (int j = cash;j>=v[i];j--)dp[j] = max(dp[j],dp[j-v[i]]+v[i]);printf("%d\n",dp[cash]);}
}

解法四

同样将多重背包用二进制思想化成01背包再用解法一的思想..
跑了63MS….生命不息,优化不止!

#include<stdio.h>
#include<cmath>
#include<string.h>
#include<iostream>
using namespace std;
int cash,n,v[10100],dp[1000001];//数组要开的足够大
int main()
{while(scanf("%d",&cash) != EOF){memset(dp,0,sizeof(dp));memset(v,0,sizeof(v));scanf("%d",&n);int cnt = 0;for(int i = 1; i <= n ; i ++){int a,b,t = 1;scanf("%d%d",&b,&a);if(b != 0){while(t < b)//此处是把b按照二进制分开{b = b - t;v[cnt++] = a * t;t *= 2;}v[cnt++] = b*a;}}if(n == 0 &&cash == 0){printf("0\n");continue;}dp[0] = 1;for(int i = 0 ; i < cnt ; i ++)for(int j = cash ; j >= v[i] ; j --)dp[j-v[i]] == 1?dp[j] = 1:0;for(int i = cash; i >= 0;i--){if(dp[i] == 1){printf("%d\n",i);break;}}}return 0;
}

二进制思想

通过这道题还弄明白了一点二进制思想,太专业的术语不会表述,打个例子,比如有10个价值为2的物品,用二进制思想的话就是将10用1,2,4,8来组成…,10-1=9,9-2=7,7-4=3,3<8,所以可以用1,2,3,4来组成10,那此时他们的价值就是2,4,6,8,相当于将这10件价值为2的物品转化成了体积分别为1,2,3,4,价值分别为2,4,6,8的四件物品了!哈哈是不是很神奇?

POJ1276 多重背包DP 生命不息优化不止相关推荐

  1. 为了OFFER,菜鸟的我必须搞懂动态规划系列三个背包问题之多重背包(二进制优化方法)

    @Author:Runsen @Date:2020/9/17 多重背包有三层循环,如果数据非常的大,那么程序就会变得非常悲伤.在多重背包的问题,其实更多的是考查多重背包的二进制优化方法.学习二进制优化 ...

  2. 多重背包单调队列优化思路_动态规划入门——多重背包与单调优化

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是算法与数据结构的第14篇文章,也是动态规划专题的第三篇. 在之前的文章当中,我们介绍了多重背包的二进制拆分的解法.在大多数情况下,这种 ...

  3. 动态规划入门——多重背包与单调优化

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是算法与数据结构的第14篇文章,也是动态规划专题的第三篇. 在之前的文章当中,我们介绍了多重背包的二进制拆分的解法.在大多数情况下,这种 ...

  4. 多重背包单调队列优化思路_多重背包问题

    题目描述: 无优化版本: int main(){int m, n;cin >> n >> m;for(int i = 1; i <= n; ++i){int v , w, ...

  5. Dividing(HDU 1059)(多重背包_二进制优化)

    题目链接: 题意:有价值分别为1,2,3,4,5,6的marbles(大理石)若干,问是否能使这些marbles平分.[marbles总数不超过2e4(很明显这是一个大常数,所以用二进制优化来做)] ...

  6. 多重背包单调队列优化思路_多重背包之单调队列优化理论性总结

    多重背包之单调队列优化: 若用F[j]表示对容量为j的背包,处理完前i种物品后,背包内物品可达到的最大总价值,并记m = min(n, j / v).放入背包的第i种物品的数目可以是:0.1.2--, ...

  7. 多重背包2[二进制位优化]

    数据范围加强一下 0<N≤1000 0<V≤2000 0<vi,wi,si≤2000 这时候O(n3)O(n^3)O(n3)的算法不行,需要优化成O(N∗logs∗V)O(N*log ...

  8. 贪吃的大嘴 多重背包 dp

    7-4 贪吃的大嘴 (15分) 有一只特别贪吃的大嘴,她很喜欢吃一种小蛋糕,而每一个小蛋糕有一个美味度,而大嘴是很傲娇的,一定要吃美味度和刚好为m的小蛋糕,而且大嘴还特别懒,她希望通过吃数量最少的小蛋 ...

  9. 多重背包(二进制优化)

    问题概述:有一个容量为V的背包和n个物品,第i种物品最多有n[i]件可用,每件体积是w[i],求解将哪些物品装入背 包可使这些物品的价值尽可能接近V但不大于V(POJ1276) 输入样例:       ...

最新文章

  1. 三代测序纠错软件汇总篇
  2. SAP gateway 后台OData model data查看工具
  3. java jdbc reparecall_Java Connection.prepareCall方法代碼示例
  4. IO多路转接模型-----epoll
  5. IDEA连接mysql出现时区错误_idea连接数据库时区错误
  6. oracle form lov 查询慢
  7. 每日算法系列【LeetCode 1186】删除一次得到子数组最大和
  8. 九、K8s deployment相关操作
  9. 自定义日历控android,android 一个简单的自定义日历控件,让你掌控时间
  10. php网站设计思路,PHP开发之网页留言板的思路及设计
  11. div+css静态网页设计 web网页设计实例作业 ——中国水墨风的小学学校网站(6页) 专题网页设计作业模板 学校物静态HTML网页模板下载
  12. 一网打尽!2018网络安全事件最全的盘点
  13. 代码中出现的奇怪问题原因
  14. 第四周项目三计算并联电阻
  15. 1985:【19CSPJ普及组】加工零件
  16. esp32入门手册学习
  17. 程序员社交网站_程序员不必在社交上感到尴尬。 这里有10项社交技巧可以改善您的职业。...
  18. MySQL修改表的字段长度
  19. 数字化开采|AIRIOT智慧矿山自动化生产解决方案
  20. 十一放假如何学习Linux等知识

热门文章

  1. 学海无涯苦作舟,生活幸福如何做?
  2. 24 迷你图标免费下载
  3. jpg格式转换器下载
  4. 什么是 C/C++?
  5. python语言程序设计(MOOC 嵩天)第六章 程序整理(0225)
  6. CSDN的评论区怎么添加超链接?
  7. Selenium WebDriver 测试Chrome浏览器
  8. 幻灯片原始板式不见了计算机二级,如何将幻灯片板式改为两栏内容?
  9. nginx 安装 php
  10. Ubuntu 18.04安装ROS Melodic