1. 程式人生 > >2017 CCPC Final G-Alice’s Stamps (揹包變形 思維)

2017 CCPC Final G-Alice’s Stamps (揹包變形 思維)

HDU - 6249

題目大意:

      給你m個連續區間,讓你選取其中的k個,使其覆蓋的範圍最大。

題解:

      按照一般的揹包思路
     dp[i][j]前i個區間,選了j個的最大覆蓋範圍

     這樣的話,最後輸出dp[n][k]
     for(int i=1;i<=n;++i)
         for(int j=1;j<=k;++j)
        {
               當前點所在的區間選與不選,

              並且如果當前點所在的區間選了的話,還要記錄一下這個區間最大延伸到哪裡
       }

      要記錄的左右端有點混亂,我自己想了一下並沒有相出很好的轉移方程來。並且看了dalao們說的,這樣寫的話對了也是O(n^3)的,會T掉。

 

     所以,換一種巧妙的記錄方式

     先根據給出的區間把每個點能夠到達的最右端點記錄一下,r[i]

     然後dp[i][j]是1-i中選j段區間能夠覆蓋的最大範圍

     這樣第i個點所在的區間選與不選.

     選的話,更新r[i]時候的狀態而不是i的狀態

          dp[r[i]][j]=max(dp[r[i]][j],dp[i][j]+r[i]-i+1)

    當然dp[i][j]=max(dp[i][j],dp[i-1][j])

 

    感覺通過這道題,對揹包又有了進一步的理解,不失為一道好題。屬於ccpc final的銅牌題

#include<bits/stdc++.h>
#include<cstring>
#define ll long long
#define INF 1000000007
#define eps 1e-7
#define mod 1000000007
using namespace std;
int r[2010];
int dp[2010][2010];
int main()
{
    //freopen("input.txt","r",stdin);
    int T,n,m,k,x,y;
    cin>>T;
    for(int cas=1;cas<=T;++cas)
    {
        cin>>n>>m>>k;
        memset(r,0,sizeof(r));
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=m;++i)
        {
            cin>>x>>y;
            for(int j=x;j<=y;++j)
                r[j]=max(r[j],y);
        }
        for(int i=1;i<=n;++i)
            for(int j=1;j<=k;++j)
        {
            dp[i][j]=max(dp[i][j],dp[i-1][j]);
            if(r[i]>0)
                dp[r[i]][j]=max(dp[r[i]][j],dp[i-1][j-1]+r[i]-i+1);
        }
        cout<<"Case #"<<cas<<": "<<dp[n][k]<<endl;
    }
    return 0;
}