【codevs】线段覆盖系列 贪心&&动规

来源:互联网 发布:和男友过夜 知乎 编辑:程序博客网 时间:2024/05/17 03:25

线段覆盖

贪心,不说了。

代码:

#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;}/*107 8 11 7 20 3 34 9 40 6 55 7 60 3 77 8 87 10 94 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;}/*5233 2333 1100 1000 2123 321 3444 555 412 450 5*/

总结:序列型动规应该是比较简单的,但刷多了还是恶心…

1 0
原创粉丝点击