题目大意:有n支施工队,m个避难处,每支施工队和每个避难处都有一个整数值,这个值表示该队伍或者该避难处距离原点的距离。现在要求将这n支施工队放入这m个避难所中(每个避难所至少有一只队伍),施工队伍进入避难所需要花费的值为该施工队伍和该避难所的距离差的绝对值,问如何分配才能使花费最小
解题思路:
要使花费最小,当然要最近找,所以先排序以下,按距离从小到大排序。
又因为要每个避难所都要有人,所以避难所要从大到小枚举,这样才能保证每个避难所都有人
设dp[i][j]为前i支队伍进入前j个避难所的最小花费值,现在增加一个新的队伍i+1,那么dp[i+1][j] = min(dp[i][j],dp[i][j-1]) + abs(队伍i的距离-避难所j的距离),第i+1个队伍必然进入第j个避难所,因为这样才能使花费达到最小
这里因为有空间大小的限制,所以要用到滚动数组来表示,具体请看代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 4010;
struct node{
int d;
int id;
int ans;
}A[MAXN],B[MAXN];
int cmp(node a,node b){
if (a.d == b.d)
return a.id < b.id;
return a.d < b.d;
}
int cmp1(node a,node b){
return a.id < b.id;
}
long long dp[MAXN];
int path[MAXN][MAXN];
int n,m;
void find_path(int i,int j){
if (i)
find_path(i-1,path[i][j]);
A[i].ans = B[j].id;
}
void init() {
for(int i = 0; i < n; i++) {
scanf("%d", &A[i].d);
A[i].id = i;
}
scanf("%d", &m);
for(int i = 0; i < m; i++) {
scanf("%d", &B[i].d);
B[i].id = i;
}
sort(A,A+n,cmp);
sort(B,B+m,cmp);
memset(dp,0x3f3f3f3f,sizeof(dp));
dp[0] = abs(A[0].d - B[0].d);
}
void solve() {
for(int i = 1; i < n; i++)
for(int j = min(m-1,i); j >= 0; j--)
if(!j || dp[j] < dp[j-1]) {
path[i][j] = j;
dp[j] = dp[j] + abs(A[i].d - B[j].d);
}
else {
path[i][j] = j - 1;
dp[j] = dp[j-1] + abs(A[i].d - B[j].d);
}
}
void print() {
printf("%lld\n", dp[m-1]);
find_path(n-1,m-1);
sort(A,A+n,cmp1);
printf("%d", A[0].ans+1);
for(int i = 1; i < n; i++)
printf(" %d", A[i].ans + 1);
printf("\n");
}
int main(){
while (scanf("%d",&n) != EOF){
init();
solve();
print();
}
return 0;
}