P1868 饥饿的奶牛
\(\color{Red}{------------------分割线----------------------------}\)
\(感觉是非常巧妙的DP。。。。\)
\(先按照区间右端点进行排序\)
\(f[i]表示前i个区间中,最后一个区间选的i,最大有多少草\)
\(f[i]=max(f[i-1],f[j]+a[i].tail-a[i].head+1);//j表示尾小于当前区间头且f最大的区间下标\)
\(这是O(n^2)的,但是找j可以用二分优化成O(nlogn)\)(下面还有O(n)算法)$
#include <bits/stdc++.h>
using namespace std;
const int maxn=150009;
int n,f[maxn];
struct p{
int x,y,val;
}a[maxn];
bool com(p a,p b){
return a.y<b.y;
}
int find(int l,int r,int key)
{
int ans=0;
while(r>=l)
{
int mid=(l+r)/2;
if(a[mid].y<key) ans=mid,l=mid+1;
else r=mid-1;
}
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
a[i].val=a[i].y-a[i].x+1;
}
sort(a+1,a+1+n,com);
for(int i=1;i<=n;i++)
{
int j=find(1,i-1,a[i].x);
f[i]=max(f[i-1],f[j]+a[i].val);
}
cout<<f[n];
return 0;
}
\(还可以直接枚举每个y坐标,那么转移就是从区间右端点刚好是枚举值的点转移过来\)
#include <bits/stdc++.h>
using namespace std;
const int maxn=3000010;
vector<int>bb[maxn];
int maxx,f[maxn];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int l,r;
scanf("%d%d",&l,&r);
bb[r].push_back(l);
maxx=max(maxx,r);
}
for(int i=1;i<=maxx;i++)
{
f[i]=f[i-1];//不选当前区间
for(int j=0;j<bb[i].size();j++)
{
int b=bb[i][j];
f[i]=max(f[i],f[b-1]+i-b+1);
}
}
cout<<f[maxx];
}