2020安徽省大学生程序设计大赛题解——K 农夫打狼

2020安徽省大学生程序设计大赛题解——K 农夫打狼

K 农夫打狼

题解

在这里插入图片描述

标签

排序,动态规划

分析

图K-1
图 K − 1      本 题 的 一 个 样 例 , 彩 色 十 字 为 起 点 和 终 点 图K-1 \ \ \ \ 本题的一个样例,彩色十字为起点和终点 K1    

想要让全程路径长度最短,有一点是毋庸置疑的,那就是农夫在刚到第 i i i层时,会找到第 i i i层最左 / / /最右边的狼开始,并按照从左向右 / / /从右向左的顺序杀完该层所有的狼,最后到达第 i i i层最右 / / /最左边的狼的位置。所以我们把所有点按 y y y坐标排序,对于 y y y坐标相同的点,求出 x x x坐标最小值和最大值。

图K-2
图 K − 2      在 完 成 一 层 后 , 上 楼 并 按 照 左 右 顺 序 走 完 该 楼 层 图K-2 \ \ \ \ 在完成一层后,上楼并按照左右顺序走完该楼层 K2    

农夫在杀完第 i i i层狼后,他的终点坐标一定在第 i i i层的最左边的狼处或者在最右边的狼处。这既是第 i i i层的终点,又是第 i + 1 i+1 i+1层的起点。

本来我们希望用dp[i]表示杀完前 i i i层狼所需要的最短路径的,但这样不便处理。基于上述描述,我们可以把动态规划

d p [ i ] [ j ] dp[i][j] dp[i][j]表示杀完前 i i i层的狼,并回到 j = 0 j=0 j=0(第 i i i层最左边) / / / j = 1 j=1 j=1(第 i i i层最右边),所需要的最小路程,那么 d p [ n ] [ n ] dp[n][n] dp[n][n]即为所求。

我们再考虑状态转移,这个二维数组的状态转移关系很容易得出,即 d p [ i − 1 ] [ 0 ] 、 d p [ i − 1 ] [ 1 ] dp[i-1][0]、dp[i-1][1] dp[i1][0]dp[i1][1] d p [ i ] [ 0 ] 、 d p [ i ] [ 1 ] dp[i][0]、dp[i][1] dp[i][0]dp[i][1]的状态转移。

具体的状态转移方程如下,至于是如何得出的,再看完程序后自然能够理解。

dp[i][0] = min(dp[i - 1][0] + abs(maxx[i - 1] - minx[i]), 
	dp[i - 1][1] + abs(maxx[i - 1] - minx[i])) + dis + 1;
dp[i][1] = min(dp[i - 1][0] + abs(minx[i - 1] - maxx[i]), 
	dp[i - 1][1] + abs(maxx[i - 1] - maxx[i])) + dis + 1;
参考答案(C++)
#include<bits/stdc++.h>
using namespace std;

struct point {
	int x, y;
	point() {}
	point(int x, int y) {
		this->x = x;
		this->y = y;
	}
}p[100005];

bool cmp(point a, point b) {
	return a.y == b.y ? a.x < b.x : a.y < b.y;
}

int dp[100005][2];
int minx[100005], maxx[100005];
int m, n, x, y;

int main() {
	cin >> n >> m;
	minx[0] = maxx[0] = 0;
	for (int i = 1; i < n; i++) {
		maxx[i] = -1;
		minx[i] = 100005;
	}
	minx[n] = maxx[n] = n;
	for (int i = 0; i < m; i++) {
		cin >> x >> y;
		p[i] = point(x, y);
	}
	sort(p, p + m, cmp);
	for (int i = 0; i < m; i++) {
		minx[p[i].y] = min(minx[p[i].y], p[i].x);
		maxx[p[i].y] = max(maxx[p[i].y], p[i].x);
	}
	for (int i = 1; i <= n; i++) {
		if (minx[i] == 100005) {
			minx[i] = minx[i - 1];
			maxx[i] = maxx[i - 1];
		}
	}
	for (int i = 1; i <= n; i++) {
		int dis = maxx[i] - minx[i];
		dp[i][0] = min(dp[i - 1][0] + abs(maxx[i - 1] - minx[i]), 
			dp[i - 1][1] + abs(maxx[i - 1] - minx[i])) + dis + 1;
		dp[i][1] = min(dp[i - 1][0] + abs(minx[i - 1] - maxx[i]), 
			dp[i - 1][1] + abs(maxx[i - 1] - maxx[i])) + dis + 1;
	}
	cout << min(dp[n][0], dp[n][1]) << endl;
	return 0;
}
  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值