bzoj1605 洛谷2905 [Usaco2008 Open]Crisis on the Farm 牧场危机(DP)

很暴力的一个题
这个题目大概分为以下几步

一、计算从原点走多少步产生的贡献

曼哈顿距离搞一搞

二、进行DP求最大值

按照字典序贪心的按WSNE进行DP,取最后一个

三、检查合格的终止点

便利一遍DP数组即可

四、倒序求出路径

通过前后产生的差量判断是否可以转移

五、输出路径

按照ans的偏移量进行输出

End.

解析见注释
代码(换码风丑死了)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int maxn = 64;
const int N = 1007;
const int INF = 2147483647;
int n,m,k,got,dp[maxn][maxn][maxn],cnt[maxn][maxn];
int dx[4]={1,0,0,-1};
int dy[4]={0,1,-1,0};
struct Point{
	int x,y;
};
Point s1[N],s2[N];
char ans[maxn][maxn][maxn],out[5]="WSNE";// 贪心的out数组 
void get_IN(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;i++)scanf("%d%d",&s1[i].x,&s1[i].y);
	for(int i=1;i<=m;i++)scanf("%d%d",&s2[i].x,&s2[i].y);
	memset(ans,'Z',sizeof(ans));
	memset(dp,-100,sizeof(dp));
	dp[0][31][31]=0;
}

void cal_DONATE(){
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			if(abs(s1[i].x-s2[j].x)>k||abs(s1[i].y-s2[j].y)>k)continue;// 曼哈顿距离 
			cnt[31+s1[i].x-s2[j].x][31+s1[i].y-s2[j].y]++;
		}
}
void cal_DP(){
	for(int p=1;p<=k;p++)
		for(int i=1;i<=62;i++)
			for(int j=1;j<=62;j++){
				dp[p][i][j]=max(max(dp[p-1][i-1][j],dp[p-1][i+1][j]),max(dp[p-1][i][j+1],dp[p-1][i][j-1]))+cnt[i][j];//   四个方向取最优 
				if(p==k)got=max(got,dp[p][i][j]);// 计算最大值 
			}
}

void Check(){
	for(int i=1;i<=62;i++)
		for(int j=1;j<=62;j++)
			if(dp[k][i][j]==got)ans[k][i][j]='A';// 是最大就标记 
}


void get_EDGE(){
	for(int p=k-1;p>=0;p--)
		for(int i=1;i<=62;i++)
			for(int j=1;j<=62;j++)
				for(int r=0;r<=3;r++)
					if(dp[p][i][j]+cnt[i+dx[r]][j+dy[r]]==dp[p+1][i+dx[r]][j+dy[r]]&&ans[p+1][i+dx[r]][j+dy[r]]<'Z')// 前一个点可以转移且转移的差值等于增值 
						ans[p][i][j]=out[r];//  符合条件就储存,out的顺序保证了,最后一个的字典序最优 
}

void slove(){
	cal_DONATE();
	cal_DP();
	Check();
    get_EDGE();
}

void Cout(){
	int p=31,q=31;// 防止负下标的平移量 
	printf("%d\n",got);
	for(int i=0;i<=k-1;i++){
		printf("%c",ans[i][p][q]);
		switch(ans[i][p][q]){// 按照偏移量进行输出 
			case'E':p--;break;
			case'W':p++;break;
			case'S':q++;break;
			case'N':q--;
		}
	}
}
int main(){
	get_IN();
	slove();
	Cout();	 
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值