[RQNOJ] P569 Milking Time

RQNOJ 569 Milking Time

题目描述

传送门

在一个数轴上可以摆M个线段,每个线段的起始终止端点给定(为整数),且每个线段有一个分值,问如何从中选取一些线段使得任意两个线段之间的距离大于R。每一条线段属于[0,N]。如何选择这些线段,使得分值之和最大?
FROM USACO

输入格式

第一行三个整数,N,M,R。M〈=1000;N〈=1000000
以下M行,每行三个整数,表示起点、终点、分值。

输出格式

一个整数,最大分值。

样例

Input

12 4 2
1 2 8
10 12 19
3 6 24
7 10 31

Output

43

主要思路

此题一看,便知是经典的dp题目——选取线段,只不过多了一个“距离大于R”。而选取线段的题目,则是由01背包扩展而来的,只不过多了端点,由点变成了线段。下面,先讲一下选取线段的题目如何想:(懂得此部分内容的大佬请略过,直接到图片下方)
如果我们将题目中的"R"删去,改为“任意两条线段都不重合”,就是传统的选取线段问题。此类题目,只需要开一个结构体存储线段的左端点(l),右端点(r)和分值(w),再对结构体按照左端点从小到大排序,就变成了背包问题。用dp[i]表示选到第i条线段时的最大分数,再枚举左端点在i之前的线段j,此时只需考虑线段j的右端点是否小于等于线段i的左端点,如果是就转移,否则不变。同时,由于不一定要选到最后一条线段,答案不一定是dp[m](例如dp[m]是负无穷),所以要每步更新ans。
转移过程如下:

到这里,唯一的问题就是新加入的距离R,为了解决这个问题,我采取了最最最智障的方法:假设每条线段比原本的线段长R,不就解决了?因此,在读入时,将线段的右端点+R即可。

代码如下

#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <ctime>
#include <map>
#include <stack>
#include <cstring>
using namespace std;
#define ll long long

struct nod{//存储线段
	int l,r,w;
}a[1005];

int n,m,r,ans=0;
int dp[1000005];

bool cmp(nod x,nod y)
{
	return x.l<y.l;
}

int main(int argc, char const *argv[])
{
	ios::sync_with_stdio(false);
	cin>>n>>m>>r;
	for(int i=1;i<=m;i++)
	{
		cin>>a[i].l>>a[i].r>>a[i].w;
		a[i].r+=r;//距离问题
	}
	sort(a+1,a+m+1,cmp);
	for(int i=1;i<=m;i++)
	{
		dp[i]=a[i].w;//首先必须选自己
		for(int j=1;j<i;j++)//由于排好序,只需枚举前面的线段即可
			if(a[j].r<=a[i].l)//如果合法
				dp[i]=max(dp[i],dp[j]+a[i].w);//转移
		ans=max(ans,dp[i]);//更新答案
	}	
	cout<<ans<<endl;//输出
	return 0;
}
posted @ 2019-12-08 17:35  WJSoj  阅读(162)  评论(0编辑  收藏  举报