P2905 [USACO08OPEN]农场危机Crisis on the Farm

传送门

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\n",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;
}

 

posted @ 2018-10-27 11:08  LLTYYC  阅读(219)  评论(0编辑  收藏  举报