[POI2009]KON-Ticket Inspector(二维前缀和+DP)
题意
有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]从哪里转移就可以得到答案了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }