poj 3616

题意: 给奶牛挤奶,共有n小时,有m次可以挤,给出每次开始挤奶的时间s,结束挤奶的时间e,还有挤奶的量f,每次挤完奶要休息r时间,问最大挤奶量.

有两种思路,
一. LIS的变形,
dp[i]表示以第i个节点结尾的最优解
1. dp[i]有两种情况,第一是的max(dp[i],d[i].f),第二就是max(dp[i],dp[j]+d[i].f),我刚开始以为if (d[i].s >= d[j].e + r) ,就是max(dp[i],dp[j]+d[i].f),else就是max(dp[i],d[i].f),其实不是这样。因为第二层循环不会刷新dp[0],所以如果dp[1]没有使用dp[0],那么d[0]的数据就会被省略,造成错误,非要这么写的话,应该在最前面对dp[0]赋值。
2. 我刚开始以为直接输出dp[m-1],dp数组的定义是以第i个节点结尾的最佳解,所以真正的最佳解有可能在中间,

#include <iostream>
#include <cstdio>
#include <iomanip>
#include <string>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <set>
#include <vector>
#include <map>
#include <algorithm>
#include <cmath>
#include <stack>
#include <stdlib.h>
#include <stdio.h>

#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
#define ll long long
#define ull unsigned long long
#define uint unsigned int
#define l(x) (x<<1)
#define r(x) (x<<1|1)
#define ms(a,b) memset(a,b,sizeof(a))

using namespace std;

struct node {
	int s, e,f;
	friend bool operator<(const node&a, const node&b) {
		return a.s < b.s;
	}
};

int n, m, r,len,res;
node d[1111111];
int dp[1111111];

int main() {
	ios::sync_with_stdio(false);
	cin >> n >> m >> r;
	for (int i = 0; i < m; i++) {
		cin >> d[i].s>> d[i].e>> d[i].f;
	}

	sort(d, d + m);

	ms(dp, 0);
	//for(int i = 0; i < m; i++) dp[i] = d[i].f;
	dp[0] = d[0].f;
	for (int i = 0; i < m; i++) {
		for (int j = 0; j < i; j++) {
			if (d[i].s >= d[j].e + r) {
				dp[i] = max(dp[i], dp[j] + d[i].f);
			}
			else {
				dp[i] = max(dp[i], d[i].f);
			}
		}
		res = max(res, dp[i]);
	}
	cout << res << endl;

	return 0;
}

二.普通的dp

dp[i]表示i时刻的最优解

  1. 上面出现的问题,
  2. 对于node的排序,应该是结束时间排序,而不是开始时间排序
#include <iostream>
#include <cstdio>
#include <iomanip>
#include <string>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <set>
#include <vector>
#include <map>
#include <algorithm>
#include <cmath>
#include <stack>
#include <stdlib.h>
#include <stdio.h>

#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
#define ll long long
#define ull unsigned long long
#define uint unsigned int
#define l(x) (x<<1)
#define r(x) (x<<1|1)
#define ms(a,b) memset(a,b,sizeof(a))

using namespace std;

struct node {
	int s, e,f;
	friend bool operator<(const node&a, const node&b) {
		return a.e < b.e;
	}
};

int n, m, r,len,res;
node d[1111111];
int dp[1111111];

int main() {
	ios::sync_with_stdio(false);
	cin >> n >> m >> r;
	for (int i = 0; i < m; i++) {
		cin >> d[i].s>> d[i].e>> d[i].f;
	}

	sort(d, d + m);

	ms(dp, 0);
	for(int i = 0; i < m; i++) dp[d[i].e] = d[i].f;
	len = 0;
	for (int i = 0; i <= n; i++) {
		dp[i] = dp[i - 1];
		for (; len < m && d[len].e <= i; len++)
			if (i > r)
				dp[i] = max(dp[i], dp[d[len].s - r] + d[len].f);
			else
				dp[i] = max(dp[i], d[len].f);
		res = max(res, dp[i]);
	}

	cout << res << endl;

	return 0;
}
POJ - 3616是一个题目,题目描述如下: 给定一组区间,每个区间有一个权重,要求选择一些区间,使得这些区间的右端点都小于等于k,并且权重之和最大。请问最大的权重和是多少? 解决这个问题的思路是使用动态规划。首先,将区间按照左端点从小到大进行排序。然后,定义一个dp数组,dp[i]表示右端点小于等于i的所有区间所能得到的最大权重。 接下来,遍历每一个区间,对于每个区间i,将dp[i]初始化为区间i的权重。然后,再遍历i之前的每个区间j,如果区间j的右端点小于等于k,并且区间j的权重加上区间i的权重大于dp[i],则更新dp[i]为dp[j]加上区间i的权重。 最后,遍历整个dp数组,找到最大的权重和,即为所求的答案。 下面是具体的代码实现: ```cpp #include <cstdio> #include <cstring> #include <algorithm> using namespace std; struct interval{ int start, end, weight; }; interval intervals[10005]; int dp[10005]; int n, m, k; bool compare(interval a, interval b) { if (a.start == b.start) { return a.end < b.end; } else { return a.start < b.start; } } int main() { while(~scanf("%d %d %d", &n, &m, &k)) { memset(dp, 0, sizeof dp); for (int i = 0; i < m; i++) { scanf("%d %d %d", &intervals[i].start, &intervals[i].end, &intervals[i].weight); } sort(intervals, intervals + m, compare); for (int i = 0; i < m; i++) { dp[i] = intervals[i].weight; for (int j = 0; j < i; j++) { if (intervals[j].end <= k && dp[j] + intervals[i].weight > dp[i]) { dp[i] = dp[j] + intervals[i].weight; } } } int maxWeight = 0; for (int i = 0; i < m; i++) { maxWeight = max(maxWeight, dp[i]); } printf("%d\n", maxWeight); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值