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;
}