描述
外星人逐渐逼近,为了保护地球,现在决定直接在外空进行战斗。
现在我们有N个导弹。需要在最短的时间内,用这N个导弹摧毁敌方n个目标(1个导弹只能摧毁1个目标)。N个导弹和目标的位置不一定相同,但是给每个导弹确定目标是一件很麻烦的事情。请你编程帮助给每个导弹确定目标,使每个导弹到其目标的距离之和最小。
格式
输入格式
第一行输入N(N<=20)
接下来N行每行包含一个坐标(x,y),表示一个导弹,-10000<x,y<10000,且x,y为整数。
接下来N行每行包含一个坐标(x,y),表示一个目标,-10000<x,y<10000,且x,y为整数。
输出格式
每个导弹到其目标距离之和的最小值。结果保留3位小数。
一开始是这样想的(错误):我先预处理出来任意两个点之间的距离,那么就是一个n*n的矩阵,(n <= 20) 那么问题转化为在这个矩阵上面选不同行不同列的n个点,且这n个点的和最小 那么就用区间DP来写 设dp[i][j][k]设以点i,j为起点,边长为k的方形中取k个点所得的最小值,那么我以为任意一个正方形都可以转化为两个成对角的小方形拼起来的,(满足不同行不同列)(如图左) 然而这个是错误的观点,反例见右图。。。。 可怜我写了半天,居然还过了5个测试点 比赛时候想法如果错了才坑 错误代码见我的代码片 正解:设DP[i][j]为用到第i个导弹时候,目标状态为j时的最优解(目标状态用二进制编码,1 代表打过,0代表没打过) 这样的话就很简单了,, 详情见代码 据说可以用随机化搜索搞 也可以用二分图
评测结果
编译成功
测试数据 #0: Accepted, time = 0 ms, mem = 209880 KiB, score = 10
测试数据 #1: Accepted, time = 0 ms, mem = 209880 KiB, score = 10
测试数据 #2: Accepted, time = 0 ms, mem = 209880 KiB, score = 10
测试数据 #3: Accepted, time = 0 ms, mem = 209880 KiB, score = 10
测试数据 #4: Accepted, time = 0 ms, mem = 209880 KiB, score = 10
测试数据 #5: Accepted, time = 0 ms, mem = 209880 KiB, score = 10
测试数据 #6: Accepted, time = 0 ms, mem = 209880 KiB, score = 10
测试数据 #7: Accepted, time = 0 ms, mem = 209880 KiB, score = 10
测试数据 #8: Accepted, time = 0 ms, mem = 209880 KiB, score = 10
测试数据 #9: Accepted, time = 0 ms, mem = 209880 KiB, score = 10
Accepted, time = 0 ms, mem = 209880 KiB, score = 100
#include
#define maxn (1 << 20) + 100
#define inf 0x3f3f3f3f
using namespace std;
struct Node {
double x;
double y;
} a[25],b[25];
double dp[25][maxn];
int cnt[maxn];
int Count1(int n) {
int cnt = 0;
while(n != 0) {
if(n % 2 == 1) cnt ++;
n /= 2;
}
return cnt;
}
double Dis(Node p,Node q) {
return sqrt((q.x - p.x) * (q.x - p.x) + (q.y - p.y) * (q.y - p.y));
}
int main() {
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int n;
scanf("%d",&n);
for(int i = 1; i <= (1 << n) - 1; i ++) cnt[i] = Count1(i);
for(int i = 1; i <= n; i ++) scanf("%lf%lf",&a[i].x,&a[i].y);
for(int i = 1; i <= n; i ++) scanf("%lf%lf",&b[i].x,&b[i].y);
for(int i = 0; i < n; i ++) dp[1][1 << i] = Dis(a[1],b[i + 1]);
for(int i = 2; i <= n; i ++) {
for(int j = 1; j <= (1 << n) - 1; j ++) {
dp[i][j] = inf;
if(cnt[j] == i) {
for(int k = 0; k < n; k ++) {
if(((1 << k) & j) == 0) continue;
int last = j - (1 << k);
dp[i][j] = min(dp[i][j],dp[i - 1][last] + Dis(a[i],b[k + 1]));
}
}
}
}
printf("%.3lf\n",dp[n][(1 << n) - 1]);
return 0;
}