12-21 tyvj水题记录(1013)(多维背包优化详解

Dec 21, 2016


“明明说好的已弃ACM了么?”

“然而看到水题根本控制不住双手T^T…正好c语言考试马上要来了, 没事刷刷水题”

tyvj是不兹词Chrome了么..登录不上, 于是手动评测(AC滑稽脸), 发现AC不了的话千万不要来找我(逃)

题目

http://www.tyvj.cn/p/1013

描述: 你有m人民币, r人品值, 你现在要追n个妹纸, 每个妹纸需要消耗一定人民币和人品, 还有一定时间, 现在你想泡到最多的妹纸, 输出你需要的最小时间.

题外: 今天龟速 + 龟量

这道题应该在很久很久以前做过, 也不知道AC过没有, 然而今天又做一遍的感受是..

还犯了很多老问题 + 各种手残

讲解一下这个题吧..虽说也不是很难, 然鹅..

题解

法一: 正常DP

正常的DP便是f[i][j], 钱为i人品为j时泡到的最多妹纸数量, mi[i][j], 钱为i, 人品为j时泡妹纸的最少所需时间。

转移方程也很好写: f[i][j] = max(f[i][j], f[i - rmb[该妹子]][j - rp[该妹子]] + 1) , 表示泡到最多的妹子

mi[i][j]随着上面的泡不泡该妹子改变, mi[i][j] = min(mi[i][j], mi[i - rmb[该妹子]][j - rp[该妹子]] + time[该妹子]), 表示泡到最多妹子所需要的时间, 且妹子相同时最小的时间

状态一路转移下来就得到答案了.

上代码:

#include <cstdio>
#include <algorithm>
using namespace std;
int rmb[1005], rp[1005], time[1005];
int f[105][105];   
int mi[105][105];
int n, m, r;
int i, v1, v2;
int main(){
	scanf("%d", &n);
	for(i = 1; i <= n; ++i)
		scanf("%d%d%d", &rmb[i], &rp[i], &time[i]);
	scanf("%d%d", &m, &r);

	for(i = 1; i <= n; ++i)
		for(v1 = m; v1 >= 0; --v1)
			for(v2 = r; v2 >= 0; --v2)
				if(v1 >= rmb[i] && v2 >= rp[i])
					if(f[v1][v2] < f[v1 - rmb[i]][v2 - rp[i]] + 1){
						f[v1][v2] = f[v1 - rmb[i]][v2 - rp[i]] + 1;
						mi[v1][v2] = mi[v1 - rmb[i]][v2 - rp[i]] + time[i];
					}else if(f[v1][v2] == f[v1 - rmb[i]][b2 - rp[i]] + 1){
						mi[v1][v2] = min(mi[v1][v2], mi[v1 - rmb[i]][v2 - rp[i]] + time[i]);
					}
	printf("%d", mi[m][r]);
	return 0;
}

法二: dp优化

在FAreStorm博客中的一种方法:

时间越少越好, 所以时间可以看作损失项

妹子越多越好, 妹子可以看做价值value

于是可以把[i][j]得到的妹子最大数f和耗费的时间mi放进一个维度里面, 用这样的一个公式:

收益 = 妹子数 * x - time * y, x,y同时要满足一个妹子的收益 > 泡100个妹子花费时间而造成的损失更高

于是最后f[i][j][k]表示泡了i个妹子, j人品, k人民币, 获得的最大收益!相当于把求解最小时间问题直接转换成了求解最大收益问题, 然后通过收益和时间之间的关系提取出时间就可以了.

f[i][j][k] = max(f[i][j][k], f[i - 1][j][k])

f[i][j + rmb[i]][k + rp[i]] = max(a[i][j + rmb[i]][k + rp[i]], a[i - 1][j][k] + 很大的数 - time[i])

代码如下:

#include <cstdio>
#include <algorithm>
#include <cmath>
#define bb 1000000
using namespace std;
int n, ans;
int rmb[105], rp[105], time[105];
int m, r;
int f[105][205][205];  // 花j元, k人品泡到的最多的妹子数量i的价值
int i, j, k;
int main()
{
    scanf("%d", &n);
    for(i = 1; i <= n; ++i){
        scanf("%d%d%d", &rmb[i], &rp[i], &time[i]);
    }
    scanf("%d%d", &m, &r);

    int maxn = 0;
    for(i = 1; i <= n; ++i)
        for(j = 0; j <= m; ++j)
            for(k = 0; k <= r; ++k){
                f[i][j][k] = max(f[i][j][k], f[i - 1][j][k]);
                f[i][j + rmb[i]][k + rp[i]] = max(f[i][j + rmb[i]][k + rp[i]], f[i - 1][j][k] + bb - time[i]);
                maxn = max(maxn, f[i][j][k]);
            }
    while(maxn > 0)
        maxn -= bb;
    printf("%d", -maxn);
    return 0;
}