题目链接:Click here~~
题意:
给出 n 个区间 [a,b],每个区间有个权值 Wi,如何取不相交的区间,使权值最大。
解题思路:
估计是一道很经典的题目,目前学习了三种解法。
1、如果区间范围比较小,例如 ZOJ 3637。则可以用 dp[i] 表示考虑到数轴中 i 点时的最大值。
存区间的时候,不再存入结构体,而是存入一个下标为起始点值的vector。当考虑到 i 点时,dp[i] 一定是最大值,然后用 dp[i] 去向后更新 dp[j]。
复杂度O(m+n)。(m 是区间总范围)
#include <vector>
#include <string>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int N = 24*60*370 + 10;
int get_month(string s)
{
if(s == "Jan") return 1;
if(s == "Feb") return 2;
if(s == "Mar") return 3;
if(s == "Apr") return 4;
if(s == "May") return 5;
if(s == "Jun") return 6;
if(s == "Jul") return 7;
if(s == "Aug") return 8;
if(s == "Sep") return 9;
if(s == "Oct") return 10;
if(s == "Nov") return 11;
if(s == "Dec") return 12;
}
int day[] = {0,31,60,91,121,152,182,213,244,274,305,335,366};
int read()
{
char str[5];
int mon,d,h,m;
scanf("%s",str);
mon = get_month(str);
scanf("%d%*s",&d);
scanf("%d:%d",&h,&m);
scanf("%s",str);
if(str[0] == 'p')
h += 12;
return m + h * 60 + (d + day[mon-1]) * 24 * 60;
}
vector< pair<int,double> > events[N];
double dp[N];
int main()
{
int n;
while(~scanf("%d",&n))
{
for(int i=0;i<N;i++)
events[i].clear();
for(int i=0;i<n;i++)
{
int t1 = read();
int t2 = read() + 5;
double val;
scanf("%lf",&val);
events[t1].push_back(make_pair(t2,val));
}
dp[0] = 0;
for(int i=0;i<N;i++){
dp[i+1] = max(dp[i+1],dp[i]);
for(int j=0;j<(int)events[i].size();j++){
int t = events[i][j].first;
dp[t] = max(dp[t],dp[i]+events[i][j].second);
}
}
printf("%.1f\n",dp[N-1]);
}
return 0;
}
2、如果区间比较大,就只能先按照区间右端点排序,然后有两种状态表示。
1> dp[i] 记录选第 i 个区间时的最大值,然后 dp[i] = max{ dp[j] + w }(j < i && i,j 不相交)。其实是这样。这是上次网络赛自己想的。复杂度O(n*n)。
2> dp[i] 记录考虑前 i 个区间的最大值,然后 dp[i] = max(dp[i-1] , dp[j] + w)(j < i && i,j 不相交)。也就是这题的做法了。
其实 2> 应该叫做 1> 的升级版。因为转移的时候,第一个是一个集合,而第二个是一个值(只要找到最大的这样的 j 就行了,因为 dp[i] >= dp[i-1])。
而这个特性,还可以通过二分,使转移的复杂度下降到O(log n),从而总复杂度 O(n*log n)。
#include <stdio.h>
#include <algorithm>
using namespace std;
const int N = 5e3 + 5;
struct Int{
int a,b,w;
bool operator < (const Int& S) const{
return b < S.b || b == S.b && a < S.a;
}
}A[N];
int find(int j)
{
int l = 1 , r = j;
while(l < r)
{
int mid = (l + r) / 2;
if(A[mid].b <= A[j].a)
l = mid + 1;
else
r = mid;
}
return r - 1;
}
int dp[N];
int main()
{
int n;
while(~scanf("%d",&n))
{
for(int i=1;i<=n;i++)
scanf("%d%d%d",&A[i].a,&A[i].b,&A[i].w);
sort(A+1,A+1+n);
dp[0] = 0;
for(int i=1;i<=n;i++){
dp[i] = max(dp[i-1],dp[find(i)]+A[i].w);
}
printf("%d\n",dp[n]);
}
return 0;
}