P3486 [POI2009]KON-Ticket Inspector

啊!这题做的真是爽!
除了DP这个方法是有提示的之外,这题居然没有题解,哈哈哈嘿嘿嘿。
很自豪的说:全是我自己独立解出来的一道题,包括设计状态,推倒(☺)转移方程,最后记录路径。

好了,首先,我们发现这题的输入贼别扭...然后我把每一行前面都加了点空格,大概就是这样:

0 2 1 8 2 1 0
   0 3 5 1 0 1
      0 3 1 2 2
         0 3 5 6
            0 3 2
               0 1

                  0

(先做一个二维前缀和再说。sum[i][j]表示i,j右上角的和,注意不是左上角)

这样就很直观了。我们可以发现,如果在第i个站点检票,就能查到sum[i][i+1]个人。特别的,查一个区域两次是不会累加的。

设计状态:f[i][j][0],f[i][j][1]分别表示前i个站台查票j次时,能查到的最大人数  与  第j次查的站。

记录一个f[i][j][1]是因为状态转移方程要用。

然后我默默的拿出草稿纸推演方程,DP题手写比干想可靠多了。

想了一天的状态转移方程,结果在纸上不到10min就ok了。

f[i][j][0]=max(f[i-1][j][0] , f[j-1......i-1][j-1][0]+sum);

令p=j-1......i-1来循环找最大值

取前者时f[i][j][1]=f[i-1][j][1];

取后者时f[i][j][1]=i;

其中sum=sum[i][i+1]-sum[  f[p][j-1][1]  ][i+1]; 看,这里就用到了f[p][j-1][1]。

然后就得出了我们的最大可查人数。

但是输出很坑,于是我写了个from[i][j]表示前i站查j次结果最大时,j-1次查的站。

然后每次记录一下from就行了。

代码如下,可以看到我的调试语句&我改动的痕迹。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 int a[602][602],sum[602][602];
 6 int f[602][52][2];///0 sum 1 last
 7 int from[602][52];
 8 int main()
 9 {
10     int n,k;
11     scanf ("%d%d",&n,&k);
12     for(int i=1;i<n;i++)
13     {
14         for(int j=i+1;j<=n;j++)///注意这里的j
15         {
16             scanf ("%d",&a[i][j]);
17         }
18     }
19     for(int i=1;i<=n;i++)///前缀和
20     {
21         for(int j=n;j>=1;j--)
22         {
23             sum[i][j]=sum[i-1][j]+sum[i][j+1]-sum[i-1][j+1]+a[i][j];
24         }
25     }
26     ///
27     for(int i=1;i<=n;i++) f[i][1][0]=sum[i][i+1],f[i][1][1]=i;///初始化
28     ///
29     for(int i=2;i<=n;i++)///前i站
30     {
31         //printf("i=%d
",i);
32         for(int j=1;j<=i && j<=k;j++)///取j次的话
33         {
34             //printf("j=%d
",j);
35             int large=-1,largep;
36             for(int p=j-1;p<i;p++)///是从p跳过来
37             {
38                 //printf("p=%d
",p);
39                 ///large=max(large,(f[p][j-1][0]+(sum[i][i+1]-sum[f[p][j-1][1]][i+1])));
40                 if(large<(f[p][j-1][0]+(sum[i][i+1]-sum[f[p][j-1][1]][i+1])))///
41                 {
42                     large=(f[p][j-1][0]+(sum[i][i+1]-sum[f[p][j-1][1]][i+1]));
43                     largep=p;
44                 }
45                 //printf("%d+(%d-%d)
",f[p][j-1][0],sum[i][i+1],sum[f[p][j+1][1]][i+1]);
46             }
47             if(large>f[i-1][j][0]) f[i][j][0]=large,f[i][j][1]=i,from[i][j]=largep;
48             else f[i][j][0]=f[i-1][j][0],f[i][j][1]=f[i-1][j][1],from[i][j]=from[i-1][j];
49             //printf("%d %d %d %d  %d
",i,j,f[i][j][0],f[i][j][1],large);
50         }
51     }
52     ///DP完毕
53     ///寻找路径
54     int d[52],top=0;
55     int p=f[n][k][1];
56     for(int i=1;i<=k;i++)
57     {
58         //printf("%d ",p);
59         d[++top]=p;
60         p=from[p][k-i+1];
61     }
62     for(int i=top;i>=1;i--) printf("%d ",d[i]);///输出
63     //printf("
%d",f[n][k][0]);
64     return 0;
65 }
66 /**
67 7 2
68   2 1 8 2 1 0
69     3 5 1 0 1
70       3 1 2 2
71         3 5 6
72           3 2
73             1
74             */
蒟蒻代码

至此,这样一道(对于我这种蒟蒻来说)十分困难的题就AC啦!(没有优化,大家有兴趣可以自己试一下能不能优化)

原文地址:https://www.cnblogs.com/huyufeifei/p/8544809.html