Another Postman Problem (邻接表+DFS)

E - Another Postman Problem
Time Limit:2000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u
Submit  Status  Practice  FZU 2038

Description

Chinese Postman Problem is a very famous hard problem in graph theory. The problem is to find a shortest closed path or circuit that visits every edge of a (connected) undirected graph. When the graph has an Eulerian Circuit (a closed walk that covers every edge once), that circuit is an optimal solution.

This problem is another version of Postman Problem. Assume there are n towns and n-1 roads, and there is a unique path between every pair of towns. There are n-1 postmen in every town, and each postman in one town regularly sends mails to one of the other n-1 towns respectively. Now, given the length of each road, you are asked to calculate the total length that all the postmen need to travel in order to send out the mails.

For example, there are six towns in the following picture. The 30 postmen should totally travel 56. The postmen in town 0 should travel 1, 2, 2, 2, 3 respectively, the postmen in town 1 should travel 1, 1, 1, 1, 2 respectively, the postmen in town 2 should travel 1, 1, 2, 2, 2 respectively, the postmen in town 3 should travel 1, 2, 3, 3, 3 respectively, the postmen in town 4 should travel 1, 2, 2, 2, 3 respectively, and the postmen in town 5 should travel 1, 2, 2, 2, 3 respectively. So the total distance is 56.

Another Postman Problem (邻接表+DFS)_第1张图片

Input

The first line of the input contains an integer T(T≤20), indicating the number of test cases. Each case begins with one integer n(n≤100,000), the number of towns. In one case, each of the following n-1 lines describes the length of path between pair a and b, with the format a, b, c(1≤c≤1000), indicating that town a and town b are directly connected by a road of length c. Note that all the n towns are numbered from 0 to n-1.

Output

For each test case, print a line containing the test case number (beginning with 1) and the total sum of the length that all postmen should travel.

Sample Input

160 1 11 2 12 3 11 4 11 5 1

Sample Output

Case 1: 56


1

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std; 
// 邻接表vector实现   
struct node
{
	int next;  
};

vector <node> map[100005];


int vis_point[100005][9]; 

__int64 edge[100005][3];


int getpoint(node pp,node pp_not)
{
	
	int i;
	int point=pp.next;
	int sum_point=0;
	int notp=pp_not.next;
	//////////////////////////////////////////////////////////////////////把递归结果保存(判断点数时不用每次重新计算)
	for (i=2;i<=8;i+=2)
	{
		if (vis_point[point][i-1]!=0 )
		{
			if (vis_point[point][i]==notp)
			{
			//		printf("used memery\n");
				return vis_point[point][i-1];
			
			}
			
		}
	}

		//////////////////////////////////////////////////////////////////////  
	if (map[point].size()>1)
	{ 
		for (i=0;i<map[point].size();i++)             ///遍历vector邻接表
		{ 
			if (notp!=map[point][i].next)					//notp指的是每一个point点对应的not_point,指该point与notp点相连,去掉他们的边时,point所在的点集合的点的总数 ,在此称为单边亲属点数                   
				sum_point+=getpoint(map[point][i],pp);        //累计
		}
		
	}
	else
	{ 
		return 1;                                         //边缘点 返回1
	}
	
	return sum_point+1;                                    //sum_point是由point递归得到的  单边亲属点数,因此 返回时要加上1代表自身   
}



int main() 
{
	
	int i,j,kk,k,t,n,x,y;
	
	__int64 w;
	scanf("%d",&t);
	for (kk=1;kk<=t;kk++)
	{
		
		
		scanf("%d",&n);
		memset(vis_point,0,sizeof(vis_point));  
		
		
		for(i=1;i<=n-1;i++) 
		{
			scanf("%d%d%I64d",&x,&y,&w);  
			edge[i][0]=x;               //记录边信息
			edge[i][1]=y;
			edge[i][2]=w;
			node a;
			a.next=x;
			map[y].push_back(a);       //构建邻接表
			a.next=y;
			map[x].push_back(a);
			
		}
		
		__int64 sum=0;
		
		for(i=1;i<=n-1;i++) 
		{
			node a;
			a.next=edge[i][0];
			node b;
			b.next=edge[i][1];
			__int64 tmp=getpoint(a,b);      //获取一条边的左点的单边亲属点数,记为x  (则另一边的单边亲属点数为n-x)
		
			
			for (j=2;j<=8;j+=2)
			{
				if (	vis_point[a.next][j]==0)      //记录左点的单边亲属点 (由于一个点只能和4个点相连,因此记录下每一个点的四对 notp点与点边亲属点  )
				{
					vis_point[a.next][j]=b.next;
					vis_point[a.next][j-1]=tmp;
					break;
				}   
				
			}

				for (j=2;j<=8;j+=2)
			{
				if (	vis_point[b.next][j]==0)	//记录右点的单边亲属点
				{
					vis_point[b.next][j]=a.next;
					vis_point[b.next][j-1]=n-tmp;
					break;
				}   
				
			}

			
			tmp=(n-tmp)*(tmp)*edge[i][2];    //经过该边的距离为 该边截断后 生成的两个点集的个数的积再乘以 边权
			sum+=tmp;    //累积
			
		}
		
		printf("Case %d: %I64d\n",kk,sum*2);               //之前算的是
		
		for(i=0;i<=n;i++)              //vector清空
	      map[i].clear();
	}
	
	
	
	return 0;
	
}

你可能感兴趣的:(Another Postman Problem (邻接表+DFS))