两道需要排序的dp背包下问题 -- hdu 2546 饭卡/ hdu 3466 Proud Merchants

来源:互联网 发布:seo关键词优化软件 编辑:程序博客网 时间:2024/06/07 20:25

饭卡  (hdu 2546)

Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 13144    Accepted Submission(s): 4550


Problem Description
电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)。所以大家都希望尽量使卡上的余额最少。
某天,食堂中有n种菜出售,每种菜可购买一次。已知每种菜的价格以及卡上的余额,问最少可使卡上的余额为多少。
 

Input
多组数据。对于每组数据:
第一行为正整数n,表示菜的数量。n<=1000。
第二行包括n个正整数,表示每种菜的价格。价格不超过50。
第三行包括一个正整数m,表示卡上的余额。m<=1000。

n=0表示数据结束。
 

Output
对于每组输入,输出一行,包含一个整数,表示卡上可能的最小余额。
 

Sample Input
1505101 2 3 2 1 1 2 3 2 1500
 

Sample Output
-4532
 

Source
UESTC 6th Programming Contest Online
 

m元买n种东西,与背包问题很相似,但是多个约束条件 当余额>5元才可以继续买。

 仅仅按照普通的背包思路问题来看的话, 假设状态"m元买n种商品所能剩下的最小余额"表示为f(n,m),那么先考虑第n种商品,最简单的情况就是第n种商品不买,那么f(n,m)=f(n-1,m)。

再看买第n种的情况,首先必须满足m>5, 假如买完后余额为负数p[n]>m显然就不用继续往下买了,此时f(n,m)=m-p[n].  

而当p[n]<=m时,正常来说应该状态转移为 f(n, m) = f(n-1, m-p[n]), 但是这里有个问题就是每次买商品都要余额大于5,那么买商品之间的顺序就会影响,比如余额11,两种商品分别需要10,5.先买第一种就不能买第二种了,先买第二种的话还能买第一种。所以挑选第n个商品可能会有后效性。考虑如果想买k种商品,最好每次买完余额尽可能大于5,也就是假如这k种能同时买的话一定是先买最小的那种,先挑价格小的就不会影响后面挑价格大的。

基于这种考虑,我们可以先对商品按价格从大到小排序,因为我们是将前n种转移到前n-1种(也就是先挑第n种)。

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;#define maxn 1005int p[maxn];int dp[maxn][maxn];int cmp(const int& a, const int&b){    return a>b;}int main(){   int n, m;   while(~scanf("%d", &n)){        if(!n) break;        for(int i = 1; i <= n; i++)            scanf("%d", p+i);        scanf("%d", &m);        sort(p+1, p+n+1, cmp);  //从大到小排序        for(int i = 0; i < maxn; i++)   //初始状态            dp[0][i]=i;        for(int i = 1; i<= n; i++)        for(int j = 1; j <= m; j++){            dp[i][j] = dp[i-1][j];  //第i种不挑            if(j>=5 && j >= p[i]) dp[i][j] = min(dp[i][j], dp[i-1][j-p[i]]); //挑第i种且余额为正            else if(j >= 5) dp[i][j] = min(dp[i][j], j-p[i]); //挑完余额为负        }        printf("%d\n", dp[n][m]);   }    return 0;}

当然这个问题还可以用一维滚动数组优化空间消耗,这里主要为了直观。




Proud Merchants(hdu 3466)

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)
Total Submission(s): 2955    Accepted Submission(s): 1224


Problem Description
Recently, iSea went to an ancient country. For such a long time, it was the most wealthy and powerful kingdom in the world. As a result, the people in this country are still very proud even if their nation hasn’t been so wealthy any more.
The merchants were the most typical, each of them only sold exactly one item, the price was Pi, but they would refuse to make a trade with you if your money were less than Qi, and iSea evaluated every item a value Vi.
If he had M units of money, what’s the maximum value iSea could get?

 

Input
There are several test cases in the input.

Each test case begin with two integers N, M (1 ≤ N ≤ 500, 1 ≤ M ≤ 5000), indicating the items’ number and the initial money.
Then N lines follow, each line contains three numbers Pi, Qi and Vi (1 ≤ Pi ≤ Qi ≤ 100, 1 ≤ Vi ≤ 1000), their meaning is in the description.

The input terminates by end of file marker.

 

Output
For each test case, output one integer, indicating maximum value iSea could get.

 

Sample Input
2 1010 15 105 10 53 105 10 53 5 62 7 3
 

Sample Output
511
 

Author
iSea @ WHU
 

Source
2010 ACM-ICPC Multi-University Training Contest(3)——Host by WHU
 

这道题目思路跟上一题思路基本一样。有m元n种商品,每种商品价值v[i],价格p[i],求能获得最大价值,约束条件是每种商品买之前余额必须大于q[i]. 同样的问题,买一种商品后改变了余额会影响其他商品的购买,也就是必须考虑购买的顺序。那么先买什么呢,答案是q[i]-p[i]最大的。 

有个比较简单粗暴的证明,假设买a,b两种商品,对应Pa,Qa. Pb,Qb。假如要先买a商品,那么最少余额为Pa+Qb,假如先买b商品,那么最少余额为 Pb+Qa. 想要先买a的话必须满足, Pa+Qb<Pb+Qa ==> Qa-Pa>Qb-Pb

其实上一题先买小的同样也是这道题目的特殊情况,只不过他们对应的Q值都是为5而已,即5-Pa>5-Pb ==> Pa<Pb


#include <iostream>#include <algorithm>#include <cstring>using namespace std;#define maxn 505struct data{    int p;    int q;    int v;    bool operator < ( const data & a) const    {        return q-p < a.q-a.p;    }};data arr[maxn];int dp[5005];   //一维滚动数组节省空间int main(){    int n, m;    while(cin >> n >> m){        memset(dp, 0, sizeof(dp));        dp[0] = 0;        for(int i = 1; i <= n; i++)            cin >> arr[i].p >> arr[i].q >> arr[i].v;            sort(arr+1, arr+n);  //按照q-p从小到大排序        for(int i = 1; i <= n; i++)        for(int j = m; j>=1; j--)            if(j >= arr[i].q && j >= arr[i].p)                dp[j] = max(dp[j], dp[j-arr[i].p]+arr[i].v);        cout << dp[m] << endl;    }    return 0;}














0 0
原创粉丝点击