对于线段覆盖1、3,贪心,每次选取末端点靠前的,O(n)扫一遍即可(话说这个题似乎排序最占时间)。(当然DP也可以)
#include<iostream>
#include<algorithm>
#define maxn 1000000+5
using namespace std;
struct line{
int b,e;
friend bool operator < (line x,line y){
if(x.e!=y.e)return x.e<y.e;
return x.b>y.b;
}
}l[maxn];
int main(){
ios::sync_with_stdio(false);
int n,i,j,k,x,y;cin>>n;
for(i=1;i<=n;i++){
cin>>x>>y;
if(x>y)swap(x,y);
l[i].b=x;l[i].e=y;
}
sort(l+1,l+n+1);
int pos=-1,cnt=0;i=1;
while(i<=n){
if(l[i].b>=pos){
cnt++;
pos=l[i].e;
}
i++;
}
cout<<cnt;
return 0;
}
但是对于线段有权值的情况,可以证明贪心是行不通的,这时候就必须DP
我的做法是:设dp[I]表示到第i条线段可获得的最大价值,容易得到这样的状态转移方程:
dp[I]=max(dp[I-1],dp[j]+line[i].c),j表示距离i最近的与i没有交集的线段。
对于查找线段j,朴素的做法就是从i开始向前枚举,然而这样只能通过线段覆盖2,对于4会TLE两个点,5会TLE7个点。
仔细分析线段性质,可以发现由于我们对线段进行了排序,所以线段的末端点对于线段的编号是具有单调性的,所以可以使用二分查找第一个满足此性质的线段。
#include<iostream>
#include<algorithm>
#define int long long
#define maxn 1000000+5
using namespace std;
struct line{
int b,e,c;//begin,end,cost
friend bool operator < (line x,line y){
if(x.e!=y.e)return x.e<y.e;
else if(x.b!=y.b)return x.b<y.b;
else return x.c<y.c;
}
}l[maxn];
int dp[maxn],x,y,z;
int bsearch(int i,bool d){
if(d==0){//二分查找
int lf=0,ri=i-1,mid;
while(lf<ri){
mid=(lf+ri)>>1;
if(l[mid].e>l[i].b){
ri=mid-1;
}
else if(l[mid].e<=l[i].b){
lf=mid+1;
}
}
if(l[lf].e<=l[i].b)return lf;
return lf-1;
}
else{//朴素查找
int p=i;
while(l[p].e>l[i].b)p--;
return p;
}
}
#undef int
int main(){
ios::sync_with_stdio(false);
int n,i,j,k;cin>>n;
for(i=1;i<=n;i++){
cin>>x>>y>>z;
l[i].b=x;l[i].e=y;l[i].c=z;
}
sort(l+1,l+n+1);
l[0].b=l[0].e=l[0].c=-1;
for(int i=1;i<=n;i++)
dp[i]=max(dp[i-1],dp[bsearch(i,0)]+l[i].c);
cout<<dp[n];
return 0;
}
然而还有另一种DP思路,(%ZY),设dp[I]表示在坐标I的最大价值,可行的一种状态转移方程为:
f[line[I].e]=max(f[line[I].e],f[line[I].b]+line[I].c);
转移时枚举line[I].e而不是每个坐标
在每次循环时,拿f[line[I].e]来更新从f[Iine[I].e+1]到f[Iine[I+1].e]中的所有点,这样可以实现在转移时选或不选这条线段的操作。
对于线段覆盖4、5,数据范围过大,需要离散化。
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
struct segment
{
long long b,e,c;
}a[1000005];
struct mes
{
long long pos,rank;
}t;
long long n,i,j,pre,p,f[1000005];
struct cmp2
{
bool operator()(mes x,mes y)
{
return x.pos>=y.pos;
}
};
priority_queue<mes,vector<mes>,cmp2> q;
bool cmp(segment x,segment y)
{
return x.e<y.e;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n;
for(i=1;i<=n;i++)
{
cin>>a[i].b>>a[i].e>>a[i].c;
q.push((mes){a[i].b,(i<<1)-1});//奇数节点为begin
q.push((mes){a[i].e,i<<1});//偶数节点为end
}
pre=q.top().pos;
for(i=1;i<=n+n;i++)
{//离散化
t=q.top();
q.pop();
if(t.pos!=pre)//如果两个点的坐标不相同
{
++p;
pre=t.pos;
}
if((t.rank&1)==1) a[(t.rank+1)>>1].b=p;
else a[(t.rank)>>1].e=p;
}
sort(a+1,a+n+1,cmp);
for(i=1;i<n;i++)
{
f[a[i].e]=max(f[a[i].e],f[a[i].b]+a[i].c);
for(j=a[i].e+1;j<=a[i+1].e;j++) f[j]=f[a[i].e];
}
f[a[n].e]=max(f[a[n].e],f[a[n].b]+a[n].c);
cout<<f[a[n].e];
}