旅行商簡化版 DP

傳送門:http://www.cqoi.net:2012/problem.php?id=1508

題目描述:

旅行商簡化版歐幾里德旅行商(Euclidean Traveling Salesman)問題也就是貨郎擔問題一直是困擾全世界數學家、計算機學家的著名問題。現有的算法都沒有辦法在確定型機器上在多項式時間內求出最優解,但是有辦法在多項式時間內求出一個較優解。 爲了簡化問題,而且保證能在多項式時間內求出最優解,J.L.Bentley提出了一種叫做bitonic tour的哈密爾頓環遊。它的要求是任意兩點(a,b)之間的相互到達的代價dist(a,b)=dist(b,a)且任意兩點之間可以相互到達,並且環遊的路線只能是從最西端單向到最東端,再單向返回最西端,並且是一個哈密爾頓迴路。現在笛卡爾平面上有n(n<=1000)個點,每個點的座標爲(x,y)(-2^31<=x<=2^31,-2^31<=y<=2^31).

輸入:

第一行一個整數n 。
接下來n行,每行兩個整數x,y,表示某個點的座標。
 輸入中保證沒有重複的兩點,且保證沒有兩點的x座標相同,並且保證最西端和最東端都只有一個點。

輸出:

一行,即最短迴路的長度,保留2位小數。

樣例輸入:

7
0 6
1 0
2 3
5 4
6 1
7 5
8 2

樣例輸出:

25.58

分析:

一道DP。
我們可以將一個人走一個經過所有點的迴路的最小路徑長度轉化爲兩個人走不重複的路並經過所有的點的最小路徑長度。
狀態轉移方程爲f[i][j]=min(f[i-1][j]+getdis(i-1,i),f[i-1][k]+getdis(k,i))。
其中f爲當前i個點都被經過一次,且靠後的人在j這個位置時所經過的路徑長度。
看起來這種做法好像會漏掉很多情況,但其實並不會。某一靠前的人在點i1直接走到i2經過的路程比較短,在i1時可能並沒有更新,但是它會在f[i2-1][k]這一步時更新。

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 1000
#define LL long long
#define INF 1e18
using namespace std;
inline double min(double a,double b){return a<b?a:b;}
pair<LL,LL>node[MAXN+5];
int n;
double getdis(int i,int j){return sqrt((node[i].first-node[j].first)*(node[i].first-node[j].first)+(node[i].second-node[j].second)*(node[i].second-node[j].second));}
double f[MAXN+5][MAXN+5],ans=INF;
void dp()
{
	int i,j;
	for(i=3;i<=n;i++)
		for(j=1;j<i;j++)
		{
			if(j==i-1)
				for(int k=1;k<i-1;k++)
					f[i][j]=min(f[i][j],f[i-1][k]+getdis(i,k));
			else
				f[i][j]=min(f[i][j],f[i-1][j]+getdis(i-1,i));
		}
}
int main()
{
	int i;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
		scanf("%I64d%I64d",&node[i].first,&node[i].second);
	sort(node+1,node+n+1);
	memset(f,0x42,sizeof f);
	f[1][1]=0;
	f[2][1]=getdis(1,2);
	dp();
	for(i=1;i<n;i++)
		ans=min(ans,f[n][i]+getdis(n,i));
	printf("%.2lf\n",ans);
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章