P7472 [NOI Online 2021 入门组] 吃豆人

· · 题解

这道题我粗看以为是DP,这让我想到方格取数和传纸条,戳这里,双倍经验。

然而当我看到数据范围,我就想应该不是DP......那就肯定是贪心了。

仔细观察会发现其实路径形成一个闭环或一个对角线,只有n条。像这样:

那就可以先暴力求出所有路径上的豆子数,然后从小到大排序,接下来就是去重的问题了。我们先关注任意两条路径相交的点,如图:

关注A,B,C,D的坐标,可以发现有X_A=Y_D,X_D=Y_A同样,X_B=Y_C,X_C=Y_BX_C=n-X_A+1同样,

注意上式只有在$(X_P+X_Q)\bmod 2=0$的时候成立,因此我们需要判一下。进而可求$B,C,D$这里有一个要点,$A,B,C,D$可能有重复的点,这时只需要判一下就可以了。 从大到小枚举所有路径,两两配对,去重后比较大小就可以了。 不过,还可以做个小优化,用$Max$存储当前最大值,如果枚举到的路径的豆子数小于当前$Max$值,就跳出循环,因为我们是从小到大枚举的。 上代码: ~~~cpp #include <iostream> #include <cstdio> #include <algorithm> #include <cmath> using namespace std; int ans=0,n,bean[1005][1005],h[5]={0,1,1,-1,-1},l[5]={0,1,-1,-1,1}; struct A { int ret,x1,y1; }add[1005]; bool cmp (A x,A y) { return x.ret>y.ret; } int main () { scanf ("%d",&n); for (int i=1;i<=n;++i) { for (int j=1;j<=n;++j) { scanf ("%d",&bean[i][j]); } } for (int i=1;i<=n;++i) { int x=1,y=i,cnt=1; add[i].x1=1; add[i].y1=i; do { add[i].ret+=bean[x][y]; if ((x==n&&y==1)||(x==n&&y==n)) break; x+=h[cnt]; y+=l[cnt]; if (x<1||x>n||y<1||y>n) { x-=h[cnt]; y-=l[cnt++]; if (cnt>4) cnt=1; x+=h[cnt]; y+=l[cnt]; } }while (x!=1||y!=i); //暴力求所有路径的豆子数 } sort (add+1,add+n+1,cmp); //从小到大排序 for (int i=1;i<=n;++i) { if (i+1<=n&&add[i].ret+add[i+1].ret<=ans) break; for (int j=i+1;j<=n;++j) { if (ans>=add[i].ret+add[j].ret) break; int Ret=add[i].ret+add[j].ret; int y=abs(add[i].y1-add[j].y1); if (y&1) ans=max(ans,Ret); else { //去重 int y2=(add[i].y1+add[j].y1)/2,x2=1+y/2; Ret-=bean[x2][y2]; if (x2!=y2) Ret-=bean[y2][x2]; int x3=n-x2+1; int y3=n-y2+1; if ((x3!=x2||y3!=y2)&&(x3!=y2||y3!=x2)) { Ret-=bean[x3][y3]; if (x3!=y3) Ret-=bean[y3][x3]; } ans=max(ans,Ret); } } } printf ("%d",ans); return 0; } ~~~