HDU - 6249 Alice’s Stamps (背包变形)

版权声明:本文为蒟蒻原创文章,转载请注明出处哦~ https://blog.csdn.net/a54665sdgf/article/details/82500538

题目链接

题意:给你m个连续区间,让你选取其中的k个,使其所包含的元素个数最多。

思路:这道题类似于二维背包,很容易想到O(n^3)的方法。但这道题给的数据范围高达2000,所以需要进行一些优化。

首先我们可以排除一些“无用”的区间,对于每个以r为右端点的区间,只保留其中左端点最小的,也就是长度最长的区间,以后只用这些区间来更新,其余的一概不用考虑。

然后,我们可以用一个数组len来记录每个右端点可以连续延伸到的左端点的最远距离。设dp[i]表示前i个区间能取得的最多的元素个数,这样我们可以得到状态转移方程:

dp[i]=max(dp[i],dp[i-len[i]]+len[i])

按照01背包的思想,我们可以从尾到头扫一遍,就相当于更新了“多放进一件物品”时的状态。

但是这样的做法可能会有遗漏,因为区间(i-len[i],i)之间的dp值可能会大于dp[i]。因此每用上面的方程从尾到头扫一遍之后,还需要从头到尾扫一遍,保证后面的dp值比前面的大。更新k次之后,dp[n]就是我们所求的结果。

这种方法比较简洁,但不是很好想,我是参考了大牛的做法进行修改的。

这道题比较考验思维的灵活性,也借此提醒下自己,平时训练的时候不能练得太死。

#define FRER() freopen("i.txt","r",stdin)
#include<bits/stdc++.h>
using namespace std;
const int N=2000+10;
int n,m,k;
int len[N],d[N],kase=0;

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&k);
        memset(len,0,sizeof len);
        memset(d,0,sizeof d);
        for(int i=1; i<=m; ++i)
        {
            int l,r;
            scanf("%d%d",&l,&r);
            for(int j=l; j<=r; ++j)len[j]=max(len[j],j-l+1);
        }
        while(k--)
        {
            for(int i=n; i>=1; --i)
                d[i]=max(d[i],d[i-len[i]]+len[i]);
            for(int i=1; i<=n; ++i)
                d[i]=max(d[i],d[i-1]);
        }
        printf("Case #%d: %d\n",++kase,d[n]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/a54665sdgf/article/details/82500538