UVA 1474(dp + 推理)

Flatland government is building a new highway that will be used to transport weapons from its main weapon plant to the frontline in order to support the undergoing military operation against its neighbor country Edgeland. Highway is a straight line and there are n construction teams working at some points on it.

During last days the threat of a nuclear attack from Edgeland has significantly increased. Therefore the construction office has decided to develop an evacuation plan for the construction teams in case of a nuclear attack. There are m shelters located near the constructed highway. This evacuation plan must assign each team to a shelter that it should use in case of an attack.

Each shelter entrance must be securely locked from the inside to prevent any damage to the shelter itself. So, for each shelter there must be some team that goes to this shelter in case of an attack. The office must also supply fuel to each team, so that it can drive to its assigned shelter in case of an attack. The amount of fuel that is needed is proportional to the distance from the team's location to the assigned shelter. To minimize evacuation costs, the office would like to create a plan that minimizes the total fuel needed.

Your task is to help them develop such a plan.

Input 

The input file contains several test cases, each of them as described below.

The first line of the input file contains n -- the number of construction teams (1$ \le$n$ \le$4000). The second line contains n integer numbers - the locations of the teams. Each team's location is a positive integer not exceeding 109, all team locations are different.

The third line of the input file contains m -- the number of shelters (1$ \le$m$ \le$n).

The fourth line contains m integer numbers -- the locations of the shelters. Each shelter's location is a positive integer not exceeding 109, all shelter locations are different.

The amount of fuel that needs to be supplied to a team at location x that goes to a shelter at location y is equal to x - y|.

Output 

For each test case, the output must follow the description below.

The first line of the output file must contain z -- the total amount of fuel needed. The second line must contain n integer numbers: for each team output the number of the shelter that it should be assigned to. Shelters are numbered from 1 to m in the order they are listed in the input file.

Sample Input 

3 
1 2 3 
2 
2 10

Sample Output 

8 
1 1 2

题意:一条公路上,n个施工队,m个避难所,要求每个避难所至少一队人,现在把施工队分配给避难所,问避难时需要的最少移动总距离。

思路:dp[i][j]代表前i个施工队放入前m个避难所,由于每个避难所至少一队人,所以dp[i][j] = min(dp[i - 1][j - 1] + abs(a[i] - b[j]), dp[i - 1][j] + abs(min(a[i] - b[k]))。由于dp[i - 1][j] + abs(min(a[i] - b[k]),要找a和b之间距离最小的,时间复杂度会增加,但是其实如果把a,b都从小到大排序后,a[i]是肯定要放入b[j]的,可以证明,如果a[i] >= b[j],a[i]到b[j]肯定最短,因为b[k < j] 小于b[j]。如果a[i] < b[j]的话,如果a[i]到b[k < j]的距离更短,那么a[k < i]到它的距离肯定也更短,如果要用其他去b[j],势必会增加大于abs(b[j] - a[i])的距离,所以还是a[i]放入b[j]是最优的。

所以最后得出结论 dp[i][j] = min(dp[i - 1][j - 1], dp[i - 1][j]) + abs(a[i] - b[j]); 然后这题数组直接开要4000*4000开不下,要用滚动数组去优化。

代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
const long long INF = 1e17;
const int N = 4005;
int n, m, i, j, ans[N];
long long dp[2][N]; 
int path[N][N];
struct Q {
	int v, id;
} a[N], b[N];

bool cmp(Q a, Q b) {
	return a.v < b.v;
}

void print(int n, int m) {
	if (n == 0 || m == 0) return;
	ans[a[n].id] = b[m].id;
	print(n - 1, m - path[n][m]);
}

long long solve() {
	for (i = 1; i <= m; i++)
		dp[0][i] = INF;
	dp[0][0] = 0;
	for (i = 1; i <= n; i++) {
		int now = i % 2;
		int pre = 1 - now;
		for (j = 0; j <= m; j++)
			dp[now][j] = INF;
		for (j = 1; j <= m && j <= i; j++) {
			if (dp[pre][j - 1] < dp[pre][j]) {
				dp[now][j] = dp[pre][j - 1] + abs(a[i].v - b[j].v);
				path[i][j] = 1;
			}
			else {
				dp[now][j] = dp[pre][j] + abs(a[i].v - b[j].v);
				path[i][j] = 0;
			}
		}
	}
	return dp[n%2][m];
}

int main() {
	while (~scanf("%d", &n)) {
		for (i = 1; i <= n; i++) {
			scanf("%d", &a[i].v);
			a[i].id = i;
		}
		scanf("%d", &m);
		for (i = 1; i <= m; i++) {
			scanf("%d", &b[i].v);
			b[i].id = i;
		}
		sort(a + 1, a + 1 + n, cmp);
		sort(b + 1, b + 1 + m, cmp);
		printf("%lld\n", solve());
		print(n, m);
		for (i = 1; i < n; i++)
			printf("%d ", ans[i]);
		printf("%d\n", ans[n]);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值