zoukankan      html  css  js  c++  java
  • 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啦!(没有优化,大家有兴趣可以自己试一下能不能优化)

  • 相关阅读:
    【BZOJ3218】 a+b Problem
    【BZOJ3993】 星际战争
    【BZOJ3991】 寻宝游戏
    【BZOJ2959】长跑 (LCT+并查集)
    NOIP2017记
    【CF 678F】Lena and Queries
    【Learning】多项式乘法与快速傅里叶变换(FFT)
    Subsequence Count (线段树)
    【留言板】
    【bzoj 4173】数学
  • 原文地址:https://www.cnblogs.com/huyufeifei/p/8544809.html
Copyright © 2011-2022 走看看