yzoj1891 最优配对问题 题解

题意

有n个点,且2|n,要求将其分为n/2对点对使得所有点对中距离之和尽量小

输出保留两位小数

考虑数据范围先想到的是搜索,然而搜索超时,我们发现在搜索的时候有重复搜索的情况,那么考虑记忆化,看到数据范围,便想到状压dp,每个点对应一个二进制位,未配对的记为1,已经配对的记为0。如n=8,未配对的点为1,3,5,7,则对应的二进制为01010101,对应的十进制为85,则把(1,3,5,7)配对的最小值存储在f[85]中。

那么我们可以写出状态转移方程

f[i]=min(f[i xor(1<<(x-1))xor(1<<(y-1))]+dis(x,y))

(i&(1<<(x-1))!=0)&(i&1<<(y-1))!=0)

然后这道题直接dp便可解决

#include<bits/stdc++.h>
using namespace std;
const int maxn=25;
const int INF=1000000;
struct node{
	int x;
	int y;
}a[maxn];
int n,S;
double dis[maxn][maxn],d[(1<<20)+5];
void ins(){
	for(int i=1;i<=n;++i){
		for(int j=i+1;j<=n;++j){
			dis[i][j]=dis[j][i]=sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y));
		}
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d %d",&a[i].x,&a[i].y);
	}
	ins();
	for(int S=1;S<=(1<<n)-1;S++){
		d[S]=INF;
		int i;
		for(i=1;i<=n-1;++i){
			if(S&(1<<(i-1))) break;
		}
		for(int j=i+1;j<=n;j++){
            if(S&(1<<(j-1))) d[S]=min(d[S],dis[i][j]+d[S^(1<<(i-1))^(1<<(j-1))]);
       	}
	}    
	printf("%.2lf",d[(1<<n)-1]);
	return 0;
}
/*
4
8730 9323
-3374 3929
-7890 -6727
1257 4689
*/
原文地址:https://www.cnblogs.com/donkey2603089141/p/11414892.html