次小生成樹--Qin Shi Huang's National Road System

Qin Shi Huang's National Road System

 

During the Warring States Period of ancient China(476 BC to 221 BC), there were seven kingdoms in China ---- they were Qi, Chu, Yan, Han, Zhao, Wei and Qin. Ying Zheng was the king of the kingdom Qin. Through 9 years of wars, he finally conquered all six other kingdoms and became the first emperor of a unified China in 221 BC. That was Qin dynasty ---- the first imperial dynasty of China(not to be confused with the Qing Dynasty, the last dynasty of China). So Ying Zheng named himself "Qin Shi Huang" because "Shi Huang" means "the first emperor" in Chinese. 


Qin Shi Huang undertook gigantic projects, including the first version of the Great Wall of China, the now famous city-sized mausoleum guarded by a life-sized Terracotta Army, and a massive national road system. There is a story about the road system: 
There were n cities in China and Qin Shi Huang wanted them all be connected by n-1 roads, in order that he could go to every city from the capital city Xianyang. 
Although Qin Shi Huang was a tyrant, he wanted the total length of all roads to be minimum,so that the road system may not cost too many people's life. A daoshi (some kind of monk) named Xu Fu told Qin Shi Huang that he could build a road by magic and that magic road would cost no money and no labor. But Xu Fu could only build ONE magic road for Qin Shi Huang. So Qin Shi Huang had to decide where to build the magic road. Qin Shi Huang wanted the total length of all none magic roads to be as small as possible, but Xu Fu wanted the magic road to benefit as many people as possible ---- So Qin Shi Huang decided that the value of A/B (the ratio of A to B) must be the maximum, which A is the total population of the two cites connected by the magic road, and B is the total length of none magic roads. 
Would you help Qin Shi Huang? 
A city can be considered as a point, and a road can be considered as a line segment connecting two points.

Input

The first line contains an integer t meaning that there are t test cases(t <= 10). 
For each test case: 
The first line is an integer n meaning that there are n cities(2 < n <= 1000). 
Then n lines follow. Each line contains three integers X, Y and P ( 0 <= X, Y <= 1000, 0 < P < 100000). (X, Y) is the coordinate of a city and P is the population of that city. 
It is guaranteed that each city has a distinct location.

Output

For each test case, print a line indicating the above mentioned maximum ratio A/B. The result should be rounded to 2 digits after decimal point.

Sample Input

2
4
1 1 20
1 2 30
200 2 80
200 1 100
3
1 1 20
1 2 30
2 2 40

Sample Output

65.00
70.00

 題意描述:
秦始皇想要修路使得n個城市連通,同時使得這些路儘量短。這時徐福說他可以在2個城市之間不花費人力物力建一條路,但他只能建1條這樣的路。秦始皇想要使除了徐福造的那條路以外的路的總長度儘量短,而徐福想要造福更多的百姓,他想要使自己用法術建的那條路的兩端的城市的總人口數最多。最終二人達成協議。假設A=用法術建的那條路的兩端的城市的總人口數,B=除了徐福造的那條路以外的路的總長度,建造的路要使得A/B最大。輸出A/B。

解題思路:(轉Noooooorth大佬)
我們可以先建一棵最小生成樹,然後再從這n個城市中選擇2個城市,把二者之間的路看做用法術造的路。如果這條路在最小生成樹上,即去掉這條邊後最小生成樹會變成兩棵獨立的樹,那麼A/B就是(二者城市的人口數總和/最小生成樹的值減去二者之間的長度);如果這條路不在生成樹上,那麼爲了使A/B最大,我們要在最小生成樹上刪除一條權值最大的邊使得生成樹分爲兩棵樹,同時這2個城市不在同一顆樹上,然後就可以把這兩個城市之間的路視爲用法術造出的路,A/B就是
(二者城市的人口數總和/最小生成樹的值減去二者之間某條使二者連通的邊的最大長度)。
綜上:我們可以使用used[i][j]表示i與j之間的邊是否在最小生成樹上,dp[i][j]表示i到j之間的使得i與j連通的權值最大的邊的權值。在prim內更新dp數組。

AC代碼: 

#include<algorithm>
#include<math.h>
#include<stdio.h>
#include<string.h>

using namespace std;

const double eps=1e-6;
const int INF=0x3f3f3f3f;
const int MAXN=1e3+20;

struct city
{
    int x,y,p;
}c[MAXN];

int V,par[MAXN];//par數組記錄生成樹上i的上一個點
bool used[MAXN][MAXN],visit[MAXN];
//used[i][j]表示i與j之間的邊是否在最小生成樹上
//visit數組用於標記一個點是否已經加入生成樹 
double dis[MAXN],cost[MAXN][MAXN],dp[MAXN][MAXN];
//dp[i][j]表示i到j之間的使得i與j連通的權值最大的邊的權值
//cost數組存放每條路的花費
//dis數組存放最小生成樹的花費 
 
double prim(){
	//初始化 
    memset(visit,false,sizeof(visit));
    memset(dp,0,sizeof(dp));
    memset(used,false,sizeof(used));
    
    for(int i=1; i<=V; i++){//初始化
        dis[i]=cost[1][i];//dis數組,因爲當前生成樹只有1號頂點故爲1號頂點到各頂點的距離
        par[i]=1;//沒有上一個,所以就是自己 
    }
    visit[1]=true;//將一號頂點加入生成樹 
    double res=0;//記錄總共花費 
    
    for(int i=1; i<V; i++){
        int v=-1;
        for(int j=1; j<=V; j++)
            if(visit[j]==0&&(v==-1||dis[j]<dis[v]))
                v=j;//找到最小的距離 
        if(v==-1)	//所有點都在最小生成樹中 
			break;
        visit[v]=true;//將找到的最小距離的點加入生成樹中 
        used[v][par[v]]=used[par[v]][v]=true;//標記該邊已在生成樹種 
        res+=dis[v];
        
        for(int j=1;j<=V;j++){
            if(visit[j]==0&&cost[v][j]<dis[j]){
            	/*
            	*	當發現原來的dis[j]的距離比現在新加入的點到j點的距離更短時
		*	就將par[j]改爲該點,那麼下次搜尋最小點的時候就搜這條,而
		*	不是原來的那條,如上所示"used[v][par[v]]=used[par[v]][v]=true" 
            	*/
                dis[j]=cost[v][j];//從點v更新到各邊的長度 
                par[j]=v;//v爲上一個點 
            }
            if(visit[j]&&j!=v){//找出v與j連通的權值最大的邊的權值
                dp[v][j]=dp[j][v]=max(dp[j][par[v]],dis[v]);
            }
        }
        
    }
    return res;//返回的結果是最小生成樹的結果 
}
 
int main()
{
    int tcase;
    scanf("%d",&tcase);
    while(tcase--){
        //輸入
        scanf("%d",&V);//V個城市
        for(int i=1; i<=V; i++)
        	scanf("%d%d%d",&c[i].x,&c[i].y,&c[i].p);
        //數據加工 
        for(int i=1; i<=V; i++)//計算兩個城市的距離,用cost數組存放 
            for(int j=1; j<=V; j++)
                cost[i][j]=cost[j][i]=sqrt((c[i].x-c[j].x)*(c[i].x-c[j].x)+(c[i].y-c[j].y)*(c[i].y-c[j].y));
        //核心代碼 
		double mst=prim();
        double ans=-1;//最終答案 
        for(int i=1; i<=V; i++){
            for(int j=1; j<=V; j++){
                if(i==j) 
                    continue;
                if(used[i][j])//i和j之間的邊是否在最小生成樹上
                /*
                *	如果這條路在最小生成樹上,即去掉這條邊後最小生成樹會
		*	變成兩棵獨立的樹,那麼A/B就是
		*	二者城市的人口數總和/最小生成樹的值減去二者之間的長度
                */	
                    ans=max(ans,(c[i].p+c[j].p+0.0)/(mst-cost[i][j])); 
                else
                /*
                *	如果這條路不在生成樹上,那麼爲了使A/B最大,我們要
		*	在最小生成樹上刪除一條權值最大的邊使得生成樹分爲兩
		*	棵樹,同時這2個城市不在同一顆樹上,然後就可以把這
		*	兩個城市之間的路視爲用法術造出的路,A/B就是
		*	二者城市的人口數總和/最小生成樹的值減去二者之間某條使二者連通的邊的最大長度
                */
                    ans=max(ans,(c[i].p+c[j].p+0.0)/(mst-dp[i][j]));
            }
        }
        printf("%.2lf\n",ans);//答案保留兩位小數 
    }
    return 0;
}

 

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