备战Noip2018模拟赛11(B组)T4 Path 好路线

10月27日备战Noip2018模拟赛11(B组)

T4路径好路线

题目描述

 

nodgd在旅游。现在,nodgd要从城市的西北角走到东南角去。这个城市的道路并不平坦,nodgd希望找出一条相对比较好走的路
.nodgd事先已经得到了这个城市的地图。地图上这个城市是一个nxm的矩形,nodgd现在站在坐标为(1,1)的位置,需要到达坐标为(n,m)的位置。这张地图上用非负整数标记了每个整数坐标点的海拔,坐标为(x,y)的位置的海拔是h(x,y).nodgd希望找出一条路线,路线中任意时刻都在向正东或向正南走,而且只在整数坐标点的地方转弯,使得路上经过的n + m -1个整数坐标点的海拔的方差最小。然而万能的nodgd当然知道该怎么走,也当然知道方差最小是多少,只是想顺便考考你。假如
有ķ个实数X1,X2,...,XK,平均值则\酒吧{X}定义为
                                               \ bar {x} = \ frac {x_ {1} + x_ {2} + ... + x_ {k}} {k}

方差\ sigma ^ {2}的定义为

                              \ sigma ^ {2} = \ frac {\ left(x_ {1}  -  \ bar {x} \ right)^ {2} + \ left(x_ {2}  -  \ bar {x} \ right)^ {2 } + ... + \ left(x_ {k}  -  \ bar {x} \ right)^ {2}} {k}

本题为了方便,只需输出\ left(n + m  -  1 \ right)^ {2} * \ sigma ^ {2}的值就可以了

输入格式

第一行输入两个整数N,M,表示城市的大小。

接下来Ñ行,每行米个数,其中第X行第ý个数就是H(X,Y)。

输出格式

输出一行一个整数,表示\ left(n + m  -  1 \ right)^ {2} * \ sigma ^ {2}的最小值。

输入样例

2 2
1 2
3 4

输出样例

14

样例解释

有两条路1-2-4和1-3-4,方差都等于14/9,所以方差最小值是14/9,输出14。

数据范围

对于30%的数据,1≤n,m≤10;

对于50%的数据,1≤n,m≤20;

对于100%的数据,1≤n,m≤50,0≤h(X,Y)≤50


思路

DP

dp [i] [j] [k]表示在第i行,第j列,海拔和为k的海拔的平方的最大和

dp [i] [j] [k] = min(dp [i] [j - 1] [k - h [i] [j]],dp [i - 1] [j] [k - h [i] [j]])+ h [i] [j] * h [i] [j];

至于平方和和方差之间的关系,稍微算一算就十分显然了。

代码

#include <iostream>
#include <cstdio>

using namespace std;

const int MAXN = 55;
const int MAXS = 5005;
const int INF = 0x3fffffff;

int n, m, tmp, last;
int dp[MAXN][MAXN][MAXS];
int h[MAXN][MAXN], Max[MAXN][MAXN], Min[MAXN][MAXN];
long long ans;

inline int read ();

int  main ()
{
	freopen("path.in", "r", stdin);
	freopen("path.out", "w", stdout);
	
	n = read (), m = read ();
	for (int i = 1; i <= n; ++ i){
		for (int j = 1; j <= m; ++ j){
			h[i][j] = read ();
			Max[i][j] = max (Max[i - 1][j], Max[i][j - 1]) + h[i][j];
			Min[i][j] = min (Min[i - 1][j], Min[i][j - 1]) + h[i][j];
		}
	}
	
	for (int i = 0; i <= n; ++ i){
		for (int k = 0; k <= Max[n][m]; ++ k){
			dp[i][0][k] = INF;
		}
	}
	for (int j = 0; j <= m; ++ j){
		for (int k = 0; k <= Max[n][m]; ++ k){
			dp[0][j][k] = INF;
		}
	}
	dp[0][1][0] = 0;
	dp[1][0][0] = 0;
	ans = 9223372036854775800;
	
	for (int i = 1; i <= n; ++ i){
		for (int j = 1; j <= m; ++ j){
			tmp = h[i][j] * h[i][j];
			for (int k = 0; k < Min[i][j]; ++ k){
				dp[i][j][k] = INF;
			}
			for (int k = Max[i][j] + 1; k <= Max[n][m]; ++ k){
				dp[i][j][k] = INF;
			}
			
			for (int k = Min[i][j]; k <= Max[i][j]; ++ k){
				last = k - h[i][j];
				dp[i][j][k] = min (dp[i - 1][j][last], dp[i][j - 1][last]) + tmp;
			}
		}
	}
	
	for (int k = Min[n][m]; k <= Max[n][m]; ++ k){
		//printf ("ans = %d, k = %d\n", ans, k);
		//printf ("dp[n][m][k] = %d\n", dp[n][m][k]); 
		ans = min (ans, 1ll * dp[n][m][k] * (n + m - 1) - 1ll * k * k);
	}
		
	printf ("%d", ans);
	
	fclose(stdin);
	fclose(stdout);
	return 0;
 } 
 
 inline int read ()
{
	char ch = getchar ();
	while (!isdigit (ch)) ch = getchar ();
	int x = 0;
	while (isdigit (ch)) {
		x = x * 10 + ch - '0';
		ch = getchar ();
	}
	return x;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值