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

dp,阶段就是步数。

本蒟蒻想不出来,只好用题解大法:四个方向的走路,dp的阶段是步数,每个点可以由四个方向转移而来。这是逆序做的。他这样做的好吃是最后回到原点,然后逆序找字典序最小即可。

独立思考下,如果从原点出发,那么向四个方向走,需要找出最大值和字典序最小的路径。因此,对每个点,值变化的时候更新值,值不变但路径更小的时候也更新。

#include<cstdio>
#include<string>
#include<iostream>
using namespace std;
const int N=1000,T=31;
const int dx[]={1,0,0,-1},dy[]={0,1,-1,0}; 
string d="ENSW";
//方向增量按字母字典序的顺序给出:E,N,S,W
int n,m,k,i,j,s,t,x[N],y[N],p[N],q[N],g[T*2][T*2],f[T][T*2][T*2],u,v,ans=0;
int ew[70],ns[70],Tk;
string st[T][T*2][T*2]; 
string minst="Z";
//因为下标不能为负,所以向南向西的负数要加上T使之为正
int max(int x,int y){return x>y?x:y;}
int abs(int x){return x>0?x:-x;}
int main(){
	for(int i=1;i<T;i++)
		for(int j=0;j<2*T;j++) 
				for(int k=0;k<2*T;k++)st[i][j][k]+='Z';
    scanf("%d%d%d",&n,&m,&k);
	Tk=	k+1;
    for(i=0;i<n;i++)
        scanf("%d%d",x+i,y+i);
    for(i=0;i<m;i++)
        scanf("%d%d",p+i,q+i);
    for(i=0;i<n;i++)
        for(j=0;j<m;j++)
            if(abs(p[j]-x[i])+abs(q[j]-y[i])<=k)
                g[p[j]-x[i]+Tk][q[j]-y[i]+Tk]++;
    for(t=0;t<k;t++)
        for(u=Tk-t;u<=Tk+t;u++)
			for(v=Tk-t;v<=Tk+t;v++){
				if(abs(u-Tk)+abs(v-Tk)>t)continue;				
				for(int i=0;i<4;i++){
					if(f[t+1][u+dx[i]][v+dy[i]]<f[t][u][v]+g[u+dx[i]][v+dy[i]]){
						f[t+1][u+dx[i]][v+dy[i]]=f[t][u][v]+g[u+dx[i]][v+dy[i]];
					    st[t+1][u+dx[i]][v+dy[i]]=st[t][u][v]+d[i];
						}
					else if(f[t+1][u+dx[i]][v+dy[i]]==f[t][u][v]+g[u+dx[i]][v+dy[i]]&&
						 st[t+1][u+dx[i]][v+dy[i]]>st[t][u][v]+d[i])
						 st[t+1][u+dx[i]][v+dy[i]]=st[t][u][v]+d[i];
					ans=max(ans,f[t+1][u+dx[i]][v+dy[i]]);
				}								   
			}
	 for(u=Tk-t;u<=Tk+t;u++)
			for(v=Tk-t;v<=Tk+t;v++)
			    if(f[k][u][v]==ans)minst=min(minst,st[k][u][v]);
	cout<<ans<<endl<<minst<<endl; 
    return 0;
}
发布了118 篇原创文章 · 获赞 25 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/lengxuenong/article/details/102535262