DP
设 f [ i ] [ j ] [ k ] 表示已经走了 i 步,向上走了 j 步,向右走了 k 步时能拯救的最多奶牛数(j,k可以为负,表示反向)
设 g [ i ] [ j ] 表示牛向上走 i 步,向右走 j 步后有多少奶牛恰好在草堆上(同样 i , j 可负)
那么 f [ i ] [ j ] [ k ] = max( f [ i-1 ] [ j -1 ] [ k ] , f [ i-1 ] [ j ] [ k-1 ] , f [ i-1 ] [ j+1 ] [ k ] ,f [ i-1 ] [ j ] [ k+1 ]) +g [ j ] [ k ]
因为数组下标不能为负,所以要把坐标集体加上一个 K
为了方便按字典序输出,我们要倒过来推,并且要按字典序的方向来枚举
即从 f [ k ] 推到 f [ 0 ] 枚举方向为 'E' 'N' 'S' 'W'
好像不太好讲,具体还是看代码吧,代码不难的
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<set> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=1007,K=31; int n,m,k; int xx[4]={1,0,0,-1},yy[4]={0,1,-1,0}; char mp[4]={'E','N','S','W'}; int f[K<<1][N][N],g[K<<1][K<<1]; int cow[N][2],hay[N][2]; int main() { n=read(); m=read(); k=read(); for(int i=1;i<=n;i++) cow[i][0]=read(),cow[i][1]=read(); for(int i=1;i<=m;i++) hay[i][0]=read(),hay[i][1]=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(abs(hay[j][0]-cow[i][0])+abs(hay[j][1]-cow[i][1])<=k)//如果可以到达才计算贡献 g[hay[j][0]-cow[i][0]+K][hay[j][1]-cow[i][1]+K]++;//预处理g for(int i=k;i>=0;i--) for(int x=K-i;x<=K+i;x++) for(int y=K-i;y<=K+i;y++)//注意大小写K的区别 { for(int o=0;o<4;o++) f[i][x][y]=max(f[i][x][y],f[i+1][x+xx[o]][y+yy[o]]); f[i][x][y]+=g[x][y]; } printf("%d ",f[0][K][K]); int posx=K,posy=K;//存当前位置 for(int i=0;i<k;i++) { int o; for(o=0;o<4;o++) if(f[i][posx][posy]==f[i+1][posx+xx[o]][posy+yy[o]]+g[posx][posy]) break;//如果是从o推过来的就断开 posx+=xx[o]; posy+=yy[o]; printf("%c",mp[o]); } return 0; }