1. 程式人生 > >[POI2009]KON-Ticket Inspector(二維前綴和+DP)

[POI2009]KON-Ticket Inspector(二維前綴和+DP)

inf 火車 pac 不同的 span ans namespace 開始 esp

題意

有n個車站,現在有一輛火車從1到n駛過,給出aij代表從i站上車j站下車的人的個數。列車行駛過程中你有K次檢票機會,所有當前在車上的人會被檢票,問最多能檢多少個不同的人的票

(n<=600,k<=50)

題解

一開始沒啥思路,然後瞄了一眼題解。看到了前綴和然後就想前綴和的意義。

結果又沒什麽收獲。絕望之際想到我瞄的那一眼,看到矩陣是倒著的,然後就有了思路。

DP也就輕而易舉地想出來了。

技術分享圖片

我們建立以左上為原點的前綴和。

然後sum[i][i+1]表示的就是經過i號站點的人數。

然後dp[i][j]代表前i個車站以第i個車站為第j個選擇的車站的最優解。

方程:

dp[i][j]=max(dp[i][j],dp[x][j-1]+sum[i][i+1]-sum[x][i+1])(0<=x<i)

然後記錄dp[i][j]從哪裏轉移就可以得到答案了。

技術分享圖片
 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 const int N=700;
 8 int n,k,a[N][N],sum[N][N],dp[N][60],ans,num,ans1,ans2[70],from[N][60];
 9 int main(){
10     scanf("
%d%d",&n,&k); 11 num=k; 12 for(int i=1;i<=n;i++){ 13 for(int j=1;j<=n-i;j++){ 14 scanf("%d",&a[i][j+i]); 15 } 16 } 17 // for(int i=1;i<=n;i++){ 18 // for(int j=1;j<=n;j++){ 19 // cout<<a[i][j]<<" "; 20 // }
21 // cout<<endl; 22 // } 23 for(int i=1;i<=n;i++){ 24 for(int j=n;j>=1;j--){ 25 sum[i][j]=sum[i-1][j]+sum[i][j+1]-sum[i-1][j+1]+a[i][j]; 26 } 27 } 28 // for(int i=1;i<=n;i++){ 29 // for(int j=1;j<=n;j++){ 30 // cout<<sum[i][j]<<" "; 31 // } 32 // cout<<endl; 33 // } 34 for(int i=0;i<=n;i++) 35 for(int j=0;j<=k;j++){ 36 dp[i][j]=-99999999; 37 } 38 dp[0][0]=0; 39 for(int i=1;i<=n;i++) 40 for(int j=1;j<=min(i,k);j++) 41 for(int x=0;x<i;x++){ 42 if(dp[i][j]<dp[x][j-1]+sum[i][i+1]-sum[x][i+1]){ 43 dp[i][j]=dp[x][j-1]+sum[i][i+1]-sum[x][i+1]; 44 from[i][j]=x; 45 } 46 } 47 for(int i=k;i<=n-1;i++){ 48 if(ans<dp[i][k]){ 49 ans1=i; 50 ans=dp[i][k]; 51 } 52 } 53 // cout<<ans<<endl; 54 while(ans1){ 55 ans2[num]=ans1; 56 ans1=from[ans1][num]; 57 num--; 58 } 59 for(int i=1;i<=k;i++){ 60 printf("%d ",ans2[i]); 61 } 62 return 0; 63 }
View Code

[POI2009]KON-Ticket Inspector(二維前綴和+DP)