2017 CCPC Final G-Alice’s Stamps (揹包變形 思維)
阿新 • • 發佈:2018-12-01
題目大意:
給你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;
}