UVA Forming Quiz Teams(10911)

题目大意:

      一场比赛中,将所有参赛选手进行分组,两个人一组,每个选手都有自己的一个坐标,要求每组中两个人之间的距离尽量接近,并且将所有小组的距离相加,使得总的的距离和最小。

 

解题思路:

      集合上的动态规划问题,需要对集合进行子集遍历。设d[S]代表集合S中的元素进行两两配对的最小距离和,则状态转移方程为:

d(S) = min{ | PiPj | + d( S - {i} - {j} )  |  j in S, i = max{S} or i = min{S} }

其中,PiPj表示选手 i 和选手 j 之间的距离,i 的取值为S中的最大元素或者最小元素,若 i 为最小元素,则 j 从 i+1开始遍历,因为 i 为最小,那么 i+1 开始,以后的元素均比 i 大,若 i 为最大元素,则 j 从 i-1开始,同理。

 

代码:

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <cmath>
#include <fstream>
#include <sstream>
#include <map>
#include <set>
#include <iomanip>
using namespace std;

#define LOOP(X,Y) for( int X = 0; X < Y; X++ )
#define LOOPP(X, Y, Z) for( int X = Y; X < Z; X++ )
#define OPENFILE(PATH) ifstream fin; fin.open(PATH)
#define PB push_back
#define ULL unsigned long long
#define LL long long


double p[21][2];
double d[99999];
double Distance[21][21];

double dist( int i, int j )
{
	return ( hypot(p[i][0] - p[j][0], p[i][1] - p[j][1]) );
}

int main()
{
	OPENFILE( "test.txt" );
	if( !fin.is_open() ) return 0;

	string line;
	string name;
	double X, Y;
	int T;
	
	istringstream is;
	while( 1 )
	{
		is.clear();
		memset( p, 0.0, sizeof( p ) );
		memset( d, 0.0, sizeof( d ) );
		getline( fin, line );
		is.str( line );
		is>>T;
		if( !T )
			break;
		T *= 2;
		LOOP( i, T )
		{
			is.clear();
			getline( fin, line );
			is.str( line );
			is>>name>>X>>Y;
			p[i][0] = X;
			p[i][1] = Y;
		}

		for( int i = 0; i < T; i++ )
		{
			for( int j = 0; j < T; j++ )
				Distance[i][j] = dist( i, j );    //实现计算出两两之间的距离
		}
		d[0] = 0;
                //用二进制进行“子集枚举”
		for( int S = 1; S < (1<<T); S++ )
		{
			int i, j;
			d[S] = 99999;
			for( i = 0; i < T; i++ )
			{
				if( S & (1<<i) )   //找出S中最小的 i
					break;
			}

			for( j = i+1; j < T; j++ )
			{
				if( S & (1<<j) )  //若 j 在 S 中
					d[S] = min( d[S], Distance[i][j] + d[S ^ (1<<i) ^ (1<<j)] );
			}
		}

		cout<<setiosflags(ios::fixed)<<setprecision(2)<<d[(1<<T)-1]<<endl;
	}

	return 0;
}

 

你可能感兴趣的:(form)