啊!这题做的真是爽!
除了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\n",i); 32 for(int j=1;j<=i && j<=k;j++)///取j次的话 33 { 34 //printf("j=%d\n",j); 35 int large=-1,largep; 36 for(int p=j-1;p<i;p++)///是从p跳过来 37 { 38 //printf("p=%d\n",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)\n",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\n",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("\n%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啦!(没有优化,大家有兴趣可以自己试一下能不能优化)