曼哈顿距离(manhattan)

**问题 C: 曼哈顿距离(manhattan)** **时间限制: 2 Sec 内存限制: 512 MB**
**题目描述** 现在我们建立一个n维的直角坐标系,每个点可以用它的坐标$(x_1,x_2,⋯,x_n)$来表示。如果我们设定每一维坐标都必须是不超过m的正整数,那么一共就有$m^n$个点。 对于两个点$(a_1,a_2,⋯,a_n)$与$(b_1,b_2,⋯,b_n)$,我们定义$∑_{i=1}^n |a_i-b_i |$,即$|a_1-b_1 |+|a_2-b_2 |+⋯+|a_n-b_n |$,为这两个点的曼哈顿距离。容易看出,两个点的距离至少为$0$,至多为$n(m-1)$。 $m^n$个点中的每个点都具有一个权值。你需要对于每个点,对于每个满足$0≤ d≤ n(m-1)$的$d$,求出和这个点距离为$d$的所有点的权值之和。(如果不存在这样的点则应输出$0$) **输入** 第一行输入两个正整数$n,m$,用空格隔开。 接下来$m^n$行,每行一个正整数,表示每个点的权值。输入是按照坐标序列的字典序排列的,即先输入点$(1,1,⋯,1,1)$的权值,再输入$(1,1,⋯,1,2)$的权值,……,再输入$(1,1,⋯,1,m)$的权值,再输入$(1,1,⋯,2,1)$的权值…… **输出** 输出$m^n$行,每行表示一个点。每行有$n(m-1)+1$个整数,依次表示和这个点距离为$0,1,⋯,n(m-1)$的所有点的权值之和。 输出的点的顺序应与输入相同;同一行相邻的两个整数之间应当用恰好一个空格隔开。 **样例输入** 2 3 9 8 7 6 5 4 3 2 1 **样例输出** 9 14 15 6 1 8 21 12 4 0 7 12 15 8 3 6 17 14 8 0 5 20 20 0 0 4 13 16 12 0 3 8 15 12 7 2 9 18 16 0 1 6 15 14 9 **提示** ![](https://img-blog.csdn.net/20180829222615800) **题解:** $dp[i][j][k]$前$i$维,第$j$个点,当前距离为$k$ 那dp转移也非常简单,直接枚举相邻的点进行转移 注意第一维要滚动数组,否则会爆空间 **Code:**
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 1000005
#define M 70005 
using namespace std;
int Pow[N],dp[2][M][205],a[N],num[M][105],n,m,x[1010];
int main()
{
	scanf("%d%d",&n,&m);
	if(n==1)
	{
		for(int i=1;i<=m;i++)scanf("%d",&x[i]);
		for(int i=1;i<=m;i++)
		{
			for(int j=0;j<m;j++)
			{
				int ans=0;
				if(i>j)ans+=x[i-j];
				if(i+j<=m&&j)ans+=x[i+j];
				if(j!=m-1)printf("%d ",ans);else
					printf("%d\n",ans);
			}
		}
	}else
    {
		int Q=1;
		for(int i=1;i<=n;i++)Q=Q*m;
		for(int i=0;i<Q;i++)
		{
			int now=n,t=i;
			for(int j=1;j<=n;j++)num[i][j]=1;
			while(t)
			{
				num[i][now]+=t%m;
				t/=m;
				now--;
			}
			scanf("%d",&dp[0][i][0]);
		}
		Pow[n]=1;
		for(int i=n-1;i;i--)Pow[i]=Pow[i+1]*m;
		for(int i=1;i<=n;i++)
		{
			for(int j=0;j<Q;j++)
				for(int k=0;k<=n*(m-1);k++)
					for(int d=0;d<m;d++)
					{
						if(k+d>n*(m-1))continue;
						if(num[j][i]+d<=m)
							dp[i%2][j][k+d]+=dp[(i-1)%2][j+d*Pow[i]][k];
						if(num[j][i]-d>0&&d)
							dp[i%2][j][k+d]+=dp[(i-1)%2][j-d*Pow[i]][k];
					}
			for(int j=0;j<Q;j++)
				for(int k=0;k<=n*(m-1);k++)
					dp[(i-1)%2][j][k]=0;
		}
		for(int i=0;i<Q;i++)
			for(int j=0;j<=n*(m-1);j++)
				if(j<n*(m-1))printf("%d ",dp[n%2][i][j]);else
					printf("%d\n",dp[n%2][i][j]);
	}
	return 0;
}
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jack-Oran

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值