1. 程式人生 > >[Wikioi 1041][NOIP 2001提高組]Car的旅行路線(疑難題)

[Wikioi 1041][NOIP 2001提高組]Car的旅行路線(疑難題)

題目描述 Description

又到暑假了,住在城市A的Car想和朋友一起去城市B旅遊。她知道每個城市都有四個飛機場,分別位於一個矩形的四個頂點上,同一個城市中兩個機場之間有一條筆直的高速鐵路,第I個城市中高速鐵路了的單位里程價格為Ti,任意兩個不同城市的機場之間均有航線,所有航線單位里程的價格均為t。

那麼Car應如何安排到城市B的路線才能儘可能的節省花費呢?她發現這並不是一個簡單的問題,於是她來向你請教。
任務
找出一條從城市A到B的旅遊路線,出發和到達城市中的機場可以任意選取,要求總的花費最少。

輸入描述 Input Description

第一行為一個正整數n(0<=n<=10),表示有n組測試資料。
每組的第一行有四個正整數s,t,A,B。
S(0<S<=100)表示城市的個數,t表示飛機單位里程的價格,A,B分別為城市A,B的序號,(1<=A,B<=S)。
接下來有S行,其中第I行均有7個正整數xi1,yi1,xi2,yi2,xi3,yi3,Ti,這當中的(xi1,yi1),(xi2,yi2),(xi3,yi3)分別是第I個城市中任意三個機場的座標,T I為第I個城市高速鐵路單位里程的價格。

輸出描述 Output Description

共有n行,每行一個數據對應測試資料。

樣例輸入 Sample Input

1
3 10 1 3
1 1 1 3 3 1 30
2 5 7 4 5 2 1
8 6 8 8 11 6 3

樣例輸出 Sample Output

47.5

資料範圍及提示 Data Size & Hint

如描述

題目思路

此題是一道典型的單源最短路問題,不過要吐槽下它的預處理太噁心了,首先輸入資料只給了每個城市的三個點的座標,第四個點的座標必須自己求(考驗幾何的時候到了),但是這四個點的相對幾何位置不固定,因此需要用迭代法不斷交換三個點的位置:A-B-C --> B-C-A --> C-A-B......直到三個點的位置如圖所示,這樣就可以求出第四個點的座標了,然後再用陣列構造一個圖,同一城市機場間的邊的長度=機場間的距離*該城市鐵路單價,不同城市的機場間的邊長=機場間的距離*飛機票單價,再用最短路演算法求解,最佳演算法是迪傑斯特拉,不過該題資料弱爆了,我用Floyd也能過。

PS:Wiki的樣例資料真TMD坑,用樣例pretest居然WA,交上去又是AC,不知道是樣例的問題還是測試點的問題,故標記此題為疑難題

#include <iostream>
#include <cstring>
#include <iomanip>
#include <cmath>
#include <algorithm>
#define INF 10000000
#define CTOA(x) (((x-1)<<2)+1) 
#define DISTANCE(x1,y1,x2,y2) ((double)sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))) //自定義巨集,開根號操作
using namespace std;
double node[4050][4050]; //node[i][j]=i點到j點的最短距離
double T; //t=飛機航線的單價,T=鐵路線路的單價
int tx[3],ty[3],t;
double ans=INF;
struct city
{
	int x[4];
	int y[4];
}ct[105]; //ct[i].x[j]=第i個城市第j個機場橫座標,ct[i].y[j]=第i個城市第j個機場縱座標
void getfour(int c) //獲取第c個城市第四個機場的座標
{
	int tt;
	memcpy(tx,ct[c].x,sizeof(tx));
	memcpy(ty,ct[c].y,sizeof(ty));
	while((tx[0]-tx[1])*(tx[2]-tx[1])+(ty[0]-ty[1])*(ty[2]-ty[1])) //(x1-x2)*(x3-x2)=(y1-y2)*(y3-y2)時,兩直線垂直
	{
		tt=tx[0]; tx[0]=tx[1]; tx[1]=tx[2]; tx[2]=tt;
		tt=ty[0]; ty[0]=ty[1]; ty[1]=ty[2]; ty[2]=tt; //點ABC轉化為BCA,不斷迭代到AB⊥BC
	}
	ct[c].x[3]=tx[0]-tx[1]+tx[2];
	ct[c].y[3]=ty[0]-ty[1]+ty[2];
}
int min(int a1,int b1)
{
	if(a1<b1) return a1;
	return b1;
}
int main()
{
	int i,j,k,l,m,n;
	scanf("%d",&n);
	while(n--)
	{
		int s,t,a,b;
		cin>>s>>t>>a>>b;
		if(a==b) { cout<<"0.0\n"; continue;} //注意出發點和目的地在同一城市的情況,直接輸出0.0
		int airport=s<<2; //airport=機場數量
		for(i=1;i<=airport;i++)
		{
			for(j=1;j<=airport;j++)
				node[i][j]=INF; //初始化各點間的路的距離為無窮大
		}
		for(i=1;i<=s;i++)
		{
			for(j=0;j<3;j++)
				cin>>ct[i].x[j]>>ct[i].y[j];
			cin>>T;
			getfour(i); //將第i個城市第四個機場確定
			for(j=0;j<4;j++)
			{
				for(k=0;k<4;k++)
					if(j!=k) //不是同一個機場的話,勾股定理初始化二者間的花費(花費=距離*每單位路程單價)
						node[CTOA(i)+j][CTOA(i)+k]=(DISTANCE(ct[i].x[j],ct[i].y[j],ct[i].x[k],ct[i].y[k])*T);
			}
		}
		for(i=1;i<=s;i++)
		{
			for(j=1;j<=s;j++)
			{
				if(i!=j) //如果i、j不是同一個城市
					for(k=0;k<4;k++) //第i個城市的第k個機場
					{
						for(l=0;l<4;l++) //第j個城市的第l個機場
							node[CTOA(i)+k][CTOA(j)+l]=(DISTANCE(ct[i].x[k],ct[i].y[k],ct[j].x[l],ct[j].y[l])*t);
					}
			}
		}
		//Floyd
		for(k=1;k<=airport;k++)
		{
			for(i=1;i<=airport;i++)
			{
				for(j=1;j<=airport;j++)
				{
					node[i][j]=min(node[i][j],node[i][k]+node[k][j]);
				}
			}
		}
		for(i=0;i<4;i++)
		{
			for(j=0;j<4;j++)
				ans=min(ans,node[CTOA(a)+i][CTOA(b)+j]);
		}
		cout<<setiosflags(ios::fixed)<<setprecision(1)<<ans<<endl;
	}
	return 0;
}