[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]从哪里转移就可以得到答案了。

 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

 

转载于:https://www.cnblogs.com/Xu-daxia/p/9432667.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值