01背包专题

题目来自:http://blog.csdn.net/libin56842/article/details/9338841

1、HDU 2602 Bone Collector

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2602

01背包裸题,注意状态压缩写法和普通写法的异同。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
int dp[1010];
int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        int n, v;
        scanf("%d %d", &n, &v);
        int i, j;
        int pri[1010], w[1010];
        for(i = 1; i <= n; i++) {
            scanf("%d", pri + i);
        }
        for(i = 1; i <= n; i++) {
            scanf("%d", w + i);
        }
        memset(dp, 0, sizeof(dp));
        for(i = 1; i <= n; i++) {  //压缩空间 
            for(j = v; j >= w[i]; j--) {
                dp[j] = max(dp[j], dp[j - w[i]] + pri[i]);
            }
        }
//        for(i = 1; i <= n; i++) {    //注意和压缩空间写法的不同之处,我还没想好怎么去解释... 
//        	for(j = 0; j <= v; j++) {
//        		if(j >= w[i]) dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + pri[i]);
//        		else dp[i][j] = d[i - 1][j];
//			}
//		}
        printf("%d\n", dp[v]);
    }
    return 0;
}


2、hdu 2546 饭卡

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2546

重量和价格相同的01背包,注意容量小于5时的特判。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
int main() {
	int n, m;
	int pri[1010], dp[1010];
	while(~scanf("%d", &n) , n) {
		int i, j;
		for(i = 0; i < n; i++) {
			scanf("%d", pri + i);
		}
		scanf("%d", &m);
		if(m < 5) {  //注意特判,小于5时什么都买不到 
			printf("%d\n", m);
			continue;
		}
		memset(dp, 0, sizeof(dp));
		sort(pri, pri + n);
		m -= 5;
		for(i = 0; i < n - 1; i++) {
			for(j = m; j >= pri[i]; j--) {
				dp[j] = max(dp[j], dp[j - pri[i]] + pri[i]);
			}
		}
		printf("%d\n", m - dp[m] + 5 - pri[n - 1]);
	}
	return 0;
}

3、hdu 1171 Big Event in HDU

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1171

题目大意:

意思好像是要分学院,然后原学院的一些器材要尽量平均分给两个学院,并且学院2的器材总价值不能超过学院1。

首先给出了总共的器材种类数n <= 50(不同的价值种类不同,并且价值相同的器材只会在一行中给出),然后下面n行每行是器材价值<=50和该类器材的数量<=100。

最后求两个学院各能分到多少价值的器材。


一道01背包题,不过这里的背包容量需要自己确定,因为要两学院尽量平分,那么一个学院分到的器材价值只要能尽量接近总器材价值的一半就可以了。所以容量即为总价值tot  除以2,这里不用担心小数,因为要求的价值都是整数,即使tot / 2取整之后求出的还是最接近的。根据题意可以知道tot 最大为250000,这样就确定了状态数,然后开始01背包,最后求出的是较小学院的价值,较大的用tot 减去即可。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
struct node {
	int v, num;
};
node fac[55];
int dp[200000];
int main() {
    int n;
    while(~scanf("%d", &n)) {
    	if(n < 0) break;
    	int i, j, k;
    	int tot = 0;
    	for(i = 1; i <= n; i++) {
    		scanf("%d %d", &fac[i].v, &fac[i].num);
    		tot += fac[i].v * fac[i].num;
		}
		int cap = tot / 2;
		memset(dp, 0, sizeof(dp));
		for(i = 1; i <= n; i++) {
			for(j = 0; j < fac[i].num; j++) {  //每个种类的每个器材都要取一遍 
				for(k = cap; k >= fac[i].v; k--) {
					dp[k] = max(dp[k], dp[k - fac[i].v] + fac[i].v);
				}
			}
		}
		printf("%d %d\n", tot - dp[cap], dp[cap]);
	}
    return 0;
}


4、hdu 1864 最大报销额

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1864

动态规划,有两种做法。

第一种就是把全部的两位小数都转化成整数,然后01背包即可。因为每张票最多报1000,最多30张,所以申请300010的空间即可。

第二种做法感觉和普通的01背包不太一样, 不太懂为什么可以这样做...好像很有道理,但是就是感觉解释不清楚....求大神解答。

做法一:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int dp[3000010];
int main(){
    double q;
    int n;
    while(~scanf("%lf %d", &q, &n) , n) {
    	int i, j, m, flag, pri[50], cnt = 0;
    	char ch;
    	double tmp, a, b, c, sum;
    	for(i = 0; i < n; i++) {
    		scanf("%d", &m);
    		flag = 1;
    		a = b = c = sum = 0;
    		for(j = 0; j < m; j++) {
    			scanf(" %c:%lf", &ch, &tmp);
    			if(ch == 'A') a += tmp;
    			else if(ch == 'B') b += tmp;
    			else if(ch == 'C') c += tmp;
    			else {
    				flag = 0;
				}
				sum += tmp;
			}
			if(flag && a <= 600 && b <= 600 && c <= 600 && sum <= 1000) {
				pri[cnt++] = sum * 100;
			}
		}
		int cap = q * 100;
		memset(dp, 0, sizeof(dp));
		for(i = 0; i < cnt; i++) {
			for(j = cap; j >= pri[i]; j--) {
				dp[j] = max(dp[j], dp[j - pri[i]] + pri[i]);
			}
		}
		double ans = dp[cap] * 1.0 / 100;
		printf("%.2lf\n", ans);
	}
    return 0;
}

做法二:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int main() {
    double q;
    int n, m;
    while(~scanf("%lf %d", &q, &n) , n) {
        int i, j;
        double sum, suma, sumb, sumc;
        char ch;
        double tmp;
        double pri[50];
        int cnt = 0;
        for(i = 0; i < n; i++) {
            scanf("%d", &m);
            getchar();
            int flag = 0;
            sum = 0,suma = 0, sumb = 0, sumc = 0;
            for(j = 0; j < m; j++) {
                ch = getchar();
                getchar();
                scanf("%lf", &tmp);
                if(ch == 'A') {
                    suma += tmp;
                }
                else if(ch == 'B') {
                    sumb += tmp;
                }
                else if(ch == 'C'){
                    sumc += tmp;
                }
                else {
                    flag = 1;
                }
                sum += tmp;
                getchar();
                if(suma > 600 || sumb > 600 || sumc > 600 || sum > 1000) {
                    flag = 1;
                }
            }
            if(flag == 0) {
                pri[cnt++] = sum;
            }
        }
        double dp[50];
        memset(dp, 0, sizeof(dp));
        for(i = 0; i < cnt; i++) {  //不太懂为什么可以这么写,好像还挺有道理,就像暴力一样 ...求解答 
        	for(j = cnt; j >= 1; j--) {
        		if(dp[j - 1] + pri[i] <= q) {
        			dp[j] = max(dp[j], dp[j - 1] + pri[i]);//dp[j]表示前i个中只取j个的最优解?? 
				}
			}
		}
		double max = 0;
		for(i = 1; i <= cnt; i++) {
			if(max < dp[i]) max = dp[i];
		}
		printf("%.2lf\n", max);
    }
    return 0;
}

5、hdu 2955 
Robberies

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2955

题目大意:

某打算去抢银行,发现被抓的总概率只要低于P就是安全的。首行T,然后每个用例首行为P和N, N表示有N家银行待抢,然后下面有N行,每行给出该银行的钱数m和抢该银行被抓的概率p,要求在保证安全的情况下最大能抢多少钱。

0 < T <= 100
0.0 <= P <= 1.0
0 < N <= 100
0 < Mj <= 100
0.0 <= Pj <= 1.0

本来以为可以像HDU 1864一样去把小数转换成整数做,但这题貌似不可以,因为精度不一定只有两位。

所以就要把抢到的钱数作为背包容量,把不被抓的概率作为价值。

那么dp[j]就表示当前情况下容量为j的背包最大的不被抓的概率。

状态转移方程:dp[j] = max(dp[j], dp[j - bank[i].m] * (1.0 - bank[i].p))

最终得到的dp[j]的含义即为最终抢到j元最大的不被抓的概率,若根本不可能抢到j元那么就等于0。

这样只需要从后往前看,第一个满足概率条件的j即为解。


dp好难....

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct node {
	int m;
	double p;
};
node bank[110];
int main() {
    int t;
    scanf("%d", &t);
    while(t--) {
    	double p;
    	int n;
    	scanf("%lf %d", &p, &n);
    	int i, j;
    	int lim = 0;
    	for(i = 1; i <= n; i++) {
    		scanf("%d %lf", &bank[i].m, &bank[i].p);
    		lim += bank[i].m;
		}
		double dp[10100];
		memset(dp, 0, sizeof(dp));
		dp[0] = 1;
		for(i = 1; i <= n; i++) {
			for(j = lim; j >= bank[i].m; j--) {
				dp[j] = max(dp[j], dp[j - bank[i].m] * (1.0 - bank[i].p));
			}
		}
		for(i = lim; i >= 0; i--) {
			if(1.0 - dp[i] < p) {
				printf("%d\n", i);
				break;
			}
		}
	}
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值