1. 程式人生 > >[JZOJ 1497] 景點中心 {樹形動態規劃}

[JZOJ 1497] 景點中心 {樹形動態規劃}

題目

DescriptionDescription
話說寧波市的中小學生在鎮海中學參加計算機程式設計比賽,比賽之餘,他們在鎮海中學的各個景點參觀。鎮海中學共有nn個景點,每個景點均有若干學生正在參觀。這nn個景點以自然數11nn編號,每兩個景點的編號均不同。每兩個景點之間有且只有一條路徑。選擇哪個景點集中的學生,才能使所有學生走過的路徑之和最小呢?

InputInput
輸入檔案center.incenter.in中有若干行:
第一行只有一個正整數nn,表示景點數。
第二行有nn1110001000間的整數,這nn個整數間互相以一個空格分隔。其中第i個整數表示第i

i個景點處的學生數。
第三行至第n+1n+1行,每行有三個整數I,j,kI,j,k,表示景點i和景點j之間有一條長尾k的路徑直接連線。其中i<>j,1in,1jn;1k1000i<>j,1≤i≤n,1≤j≤n;1≤k≤1000。

OutputOutput
輸出檔案center.out中有二行;
第一行只有一個整數i,表示在第i個景點處集中時,所有學生走過的路徑之和最短。
第二行也只有一個整數,表示所有學生走過的路徑之和的最小值

解題思路

3030分,考試程式碼,F

loydFloyd
100100分,正解,樹型動態規劃。只需先算出一次以1為根節點的樹的所有學生數量,和路徑。然後沿著這棵樹,求出假設子節點為“根節點”的樹時的值【一邊加,一邊減】。然後列舉求出最大值即可。

程式碼(30分)

#include<cstdio> 
#include<cstring>
#include<algorithm>
using namespace std; 
int n,a[10001],x,y,t[10001][10001]; 
int main()
{
	scanf("%d",&n); 
	memset(t,127/3,sizeof(t));
for (int i=1;i<=n;i++) scanf("%d",&a[i]); for (int i=1;i<=n-1;i++) { scanf("%d%d",&x,&y); scanf("%d",&t[x][y]); t[y][x]=t[x][y]; } for (int m=1;m<=n;m++) for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) if(i!=j) t[i][j]=min(t[i][j],t[i][m]+t[m][j]); int ans=1e9,minn=-1; for (int i=1;i<=n;i++) { int ma=0; for (int j=1;j<=n;j++) if (i!=j) ma+=a[j]*t[j][i]; if (ans>ma) { minn=i; ans=ma; } } printf("%d\n",minn); printf("%d",ans); }

程式碼(100分)

#include<cstdio>
#include<algorithm>
using namespace std;
struct node{
	int y,z,next; 
}a[300011];
int n,len,last[100011],w;
long long size[100011],sum[100011],q;
void add(int xx,int yy,int zz)
{ a[++len].y=yy; a[len].z=zz; a[len].next=last[xx]; last[xx]=len; }
void dp(int g,int dep,int father)
{
	size[g]=sum[g]*dep;//dep為到根節點的距離
	for (int i=last[g];i;i=a[i].next)
	 if (a[i].y!=father) 
	 {
	 	dp(a[i].y,dep+a[i].z,g);
	 	sum[g]+=sum[a[i].y]; //人數
	 	size[g]+=size[a[i].y]; //目前節點路徑總和
	 }
}
void sdp(int g,int father)
{
	for (int i=last[g];i;i=a[i].next)
	 if (a[i].y!=father) 
	 {
		size[a[i].y]=size[g]+(sum[1]-2*sum[a[i].y])*a[i].z; //換根
		if (size[a[i].y]<q)
		{
			q=size[a[i].y];
			w=a[i].y;
		}
	 	sdp(a[i].y,g); 
	 }
}
int main()
{
	scanf("%d",&n); 
	for (int i=1;i<=n;i++)
	 scanf("%d",&sum[i]); 
	int xx,yy,zz; 
	for (int i=1;i<n;i++)
	{
	 scanf("%d%d%d",&xx,&yy,&zz); 
	 add(xx,yy,zz); add(yy,xx,zz); 
    }
    q=1e18;w=-1;
	dp(1,0,0); 
    sdp(1,0); 
    if (size[1]<q)//落下的判斷
	{
		q=size[1];
		w=1;
	}
	printf("%d\n%lld",w,q); 
}