线段覆盖
贪心,不说了。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int size=1000010;
struct edge{
int l,r;
}l[size];
bool cmp(const edge &a,const edge &b)
{
return a.r<b.r;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&l[i].l,&l[i].r);
if(l[i].l>l[i].r) swap(l[i].l,l[i].r);
}
sort(l+1,l+1+n,cmp);
int ans=0;
for(int i=1,last=-2333;i<=n;i++)
{
if(last<=l[i].l)
{
last=l[i].r;
ans++;
}
}
printf("%d",ans);
return 0;
}
线段覆盖2
这个是dp。
按右端点排序。
dp[i]代表当第i个线段是被选择的最后一个线段后,所达到的最大价值。
所以状态转移方程:
dp[i]=max{dp[i],dp[j]+l[i].d} (j<i && l[i].l>=l[j].r)
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int size=23333;
int dp[size];
struct edge{
int l,r,d;
}l[size];
bool cmp(const edge &a,const edge &b)
{
return a.r<b.r;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d%d",&l[i].l,&l[i].r,&l[i].d);
sort(l+1,l+1+n,cmp);
int ans=0;
for(int i=1;i<=n;i++)
{
dp[i]=l[i].d;
for(int j=1;j<i;j++)
{
if(l[j].r<=l[i].l)
{
dp[i]=max(dp[i],dp[j]+l[i].d);
}
}
ans=max(dp[i],ans);
}
printf("%d",ans);
return 0;
}
/*
10
7 8 1
1 7 2
0 3 3
4 9 4
0 6 5
5 7 6
0 3 7
7 8 8
7 10 9
4 7 10
*/
线段覆盖3
贪心,和第一个一样。
线段覆盖4
这个也是dp,不过n太大,不能n^2。
可以改变状态的表示。
dp[i]代表选到坐标i的时候所拿到的最大价值。
状态转移方程:
dp[i]=max{ dp[i],max{ dp[1],dp[2],...,dp[i-1] }+l[i].d }
但因为是线段,所以可以这样:
dp[l[i].r]=max{ dp[i],max{dp[1],dp[2]...,dp[l[i].l]}+l[i].d }
因为要先确定dp[l[i].l]
,所以应该把坐标按左端点排序。
如何选取1~l[i].l
的最大值?
树状数组!
这样就优化到O(nlogn)
代码:
#include<cstring>
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL size=1000010;
const LL INF=(LL)1e15;
struct edge{
LL l,r;
LL d;
}l[size];
LL dp[size];
bool cmp(const edge &a,const edge &b)
{
return a.l<b.l;
}
LL bits[size];
void add(LL x,LL d)
{
for(LL i=x;i<=size-10;i+=i&(-i))
{
bits[i]=max(bits[i],d);
}
}
LL ask(LL x)
{
LL ans=0;
for(LL i=x;i>0;i-=i&(-i))
{
ans=max(ans,bits[i]);
}
return ans;
}
int main()
{
LL n;
scanf("%lld",&n);
for(LL i=1;i<=n;i++) scanf("%lld%lld%lld",&l[i].l,&l[i].r,&l[i].d);
sort(l+1,l+1+n,cmp);
LL ans=0;
for(LL i=1;i<=n;i++)
{
dp[l[i].r]=max(dp[l[i].r],ask(l[i].l)+l[i].d);
ans=max(ans,dp[l[i].r]);
add(l[i].r,dp[l[i].r]);
}
printf("%lld",ans);
return 0;
}
线段覆盖5
坐标离散化,就和4一样了。
听说卡P党?
代码:
#include<cstring>
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL size=2000010;
const LL INF=(LL)1e15;
struct edge{
LL l,r;
LL d;
}l[size];
bool cmp(const edge &a,const edge &b)
{
return a.l<b.l;
}
LL lsh[size];
LL dp[size],bits[size];
void add(LL x,LL d)
{
for(LL i=x;i<=size-10;i+=i&-i)
{
bits[i]=max(bits[i],d);
}
}
LL ask(LL x)
{
LL ans=0;
for(LL i=x;i>0;i-=i&-i)
{
ans=max(bits[i],ans);
}
return ans;
}
int main()
{
LL n;
scanf("%lld",&n);
for(LL i=1;i<=n;i++)
{
scanf("%lld%lld%lld",&l[i].l,&l[i].r,&l[i].d);
lsh[++lsh[0]]=l[i].l;
lsh[++lsh[0]]=l[i].r;
}
sort(lsh+1,lsh+1+lsh[0]);
LL len=unique(lsh+1,lsh+1+lsh[0])-lsh-1;
for(LL i=1;i<=n;i++)
{
l[i].l=lower_bound(lsh+1,lsh+1+len,l[i].l)-lsh;
l[i].r=lower_bound(lsh+1,lsh+1+len,l[i].r)-lsh;
}
//for(LL i=1;i<=n;i++) printf("%d %d %d\n",l[i].l,l[i].r,l[i].d);
sort(l+1,l+1+n,cmp);
LL ans=0;
for(LL i=1;i<=n;i++)
{
dp[l[i].r]=max(dp[l[i].r],l[i].d+ask(l[i].l));
ans=max(ans,dp[l[i].r]);
add(l[i].r,dp[l[i].r]);
}
printf("%lld",ans);
return 0;
}
/*
5
233 2333 1
100 1000 2
123 321 3
444 555 4
12 450 5
*/
总结:序列型动规应该是比较简单的,但刷多了还是恶心…