很暴力的一个题
这个题目大概分为以下几步
一、计算从原点走多少步产生的贡献
曼哈顿距离搞一搞
二、进行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;
}