DP背包(注意减少内存的转换)
快速链接:http://poj.org/problem?id=2063
CSUST 2012年暑假8月组队后第八次个人赛:http://acm.hust.edu.cn:8080/judge/contest/view.action?cid=11473#problem/D
Time Limit: 1000MS | Memory Limit: 30000K | |
Total Submissions: 5370 | Accepted: 1848 |
Description
Value | Annual interest |
4000 3000 |
400 250 |
Input
Output
Sample Input
1 10000 4 2 4000 400 3000 250
Sample Output
14050
Source
题意:
首先输入一个整数 T ,表示有 T 组测试数据。
给你一定本金 V 和存入银行的年限 year ( 也就是样例中第二行输入的数据 )
然后输入一个整数 n 表示有 n 种年利息计算法。
剩下的 n 行就是每种本金产生的年利息的钱。
算法思想:完全背包 第二个基本的背包问题模型,每种物品可以放无限多次。PS:如果你不懂什么是完全背包请看
http://blog.csdn.net/cfreezhan/article/details/7865136#_P02:_完全背包问题
完全背包状态转移方程:
for i=1..N
for v=0..V
f[v]=max{f[v],f[v-cost]+weight}
注意:转换 本题的背包容量就是本金
但是每一年都有利息产生,所以本金都会增长,从而背包的体积不断增大。
放入背包的物品的容量就是每种利息所需的本金cost[i],放入物品后产生的价值就是
利息weight[i]。
内存问题:注意题目中的本金很大但是最开始最多的本金不超过$1 000 000
而且每种计算利息所需的本金都是$1 000的倍数,所以为了避免超内存,每次
dp时先把本金除以$1 000,相应的cost也除以$1 000
那么dp数组到底开多大才合适呢,题中说最多存40年,开始时本金不超过$1 000 000而每次的利息又不会超过本金的 10% ,
所以 数组的大小应该是 $1 000 000 / 1000 * ( 1.1 ) ^40
而 1.1^40 大概为 45.259256,就取 50 吧。
从而数组开 1 000 * 50 = 50 000
//Accepted 360 KB 79 ms C++ 667 B 2013-03-14 19:26:23 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 50 * 1000; int dp[maxn]; int main() { int T; int V, year; int n; int cost[11], weight[11]; scanf("%d", &T); while(T--) { scanf("%d%d", &V, &year); int Max = V; scanf("%d", &n); for(int i = 1; i <= n; i++) { scanf("%d%d", &cost[i], &weight[i]); cost[i] /= 1000; //每种债券除以1000减少内存。 } while(year--) { V /= 1000; //相应的每次的本金除以1000减少内存 memset(dp, 0, sizeof(dp)); for(int i = 1; i <= n; i++) { for(int j = cost[i]; j <= V; j++) dp[j] = max(dp[j], dp[j-cost[i]] + weight[i]); } Max += dp[V]; //本金+利息(注意利息开始没有除以也不用除以1000,所以Max即为所求) V = Max; } printf("%d\n", V); } return 0; }
下面的是以前写的。。。思路完全一样,只是上面重做时重新分析了下数组大小问题。。。
//AC 948k 94ms C++ POJ 2063 Investment /*思路:DPz之完全背包(如果不懂完全背包的推荐看我转载的《背包九讲》的博客,推荐一道完全背包入门题目hdu 1114) 完全背包:即每种物品可以放N次。 状态转移方程:if(dp[j]<dp[j-value[i]]+interest[i]) dp[j]=dp[j-value[i]]+interest[i]; 需要注意的是,这道题目是背包体积不断增大的完全背包。 开始背包的体积是本金,每隔一年都会得到一定的利息,所以每年背包的体积都会变大。 重点:关于内存的处理,比赛时一直错就是内存没处理好,后来看了学长的题解(http://www.shabiyuan.com/?category欢迎访问)才清楚。 由于本金的数值很大,而数组又不能开的特别大。所以用dp处理时,要把每次的本金除以1000减少内存,相应的每种债券所需要的钱也除以1000 */
//400k 63ms (当数组开到maxn=60000时)
#include<cstdio> #include<cstring> const int maxn=200000; int dp[maxn]; int main() { int test; int start,year; int d; int i,j; int value[15],interest[15]; int ans,max; scanf("%d",&test); while(test--) { scanf("%d%d",&start,&year); ans=max=start; scanf("%d",&d); for(i=0;i<d;i++) { scanf("%d%d",&value[i],&interest[i]); value[i]/=1000;//每种债券除以1000减少内存。 } while(year--) { memset(dp,0,sizeof(dp)); max/=1000; //相应的每次的本金除以1000减少内存 for(i=0;i<d;i++) for(j=value[i];j<=max;j++) if(dp[j]<dp[j-value[i]]+interest[i]) dp[j]=dp[j-value[i]]+interest[i]; ans+=dp[max];//本金+利息(注意利息开始没有除以也不用除以1000,所以ans即为所求) max=ans; } printf("%d\n",ans); } return 0; }