[洛谷P4438] HNOI2018 道路


問題描述

W 國的交通呈一棵樹的形狀。W 國一共有n - 1個城市和n個鄉村,其中城市從1到n - 1 編號,鄉村從1到n編號,且1號城市是首都。道路都是單向的,本題中我們只考慮從鄉村通往首都的道路網絡。對於每一個城市,恰有一條公路和一條鐵路通向這座城市。對於城市i, 通向該城市的道路(公路或鐵路)的起點,要么是一個鄉村,要么是一個編號比ii大的城市。 沒有道路通向任何鄉村。除了首都以外,從任何城市或鄉村出發只有一條道路;首都沒有往 外的道路。從任何鄉村出發,沿着唯一往外的道路走,總可以到達首都。

W 國的國王小 W 獲得了一筆資金,他決定用這筆資金來改善交通。由於資金有限,小 W 只能翻修n - 1條道路。小 W 決定對每個城市翻修恰好一條通向它的道路,即從公路和鐵 路中選擇一條並進行翻修。小 W 希望從鄉村通向城市可以盡可能地便利,於是根據人口調 查的數據,小 W 對每個鄉村制定了三個參數,編號為ii的鄉村的三個參數是ai,bi和ci。假設 從編號為i的鄉村走到首都一共需要經過x條未翻修的公路與y條未翻修的鐵路,那么該鄉村 的不便利值為

\[c_i \cdot (a_i + x) \cdot (b_i + y) \]

在給定的翻修方案下,每個鄉村的不便利值相加的和為該翻修方案的不便利值。 翻修n - 1條道路有很多方案,其中不便利值最小的方案稱為最優翻修方案,小 W 自然 希望找到最優翻修方案,請你幫助他求出這個最優翻修方案的不便利值。

輸入格式

第一行為正整數n。

接下來n - 1行,每行描述一個城市。其中第i行包含兩個數si,ti。si表示通向第i座城市 的公路的起點,ti表示通向第i座城市的鐵路的起點。如果si > 0,那么存在一條從第si座城 市通往第i座城市的公路,否則存在一條從第-si個鄉村通往第i座城市的公路;ti類似地,如 果ti >,那么存在一條從第ti座城市通往第i座城市的鐵路,否則存在一條從第-ti個鄉村通 往第i座城市的鐵路。

接下來n行,每行描述一個鄉村。其中第i行包含三個數ai,bi,ci,其意義如題面所示。

輸出格式

輸出一行一個整數,表示最優翻修方案的不便利值。

樣例輸入

6
2 3
4 5
-1 -2
-3 -4
-5 -6
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

樣例輸出

54

解析

由題目的意思,最后構成的樹有這些特征:葉節點都是鄉村;每個城市只有兩個兒子,且一個是公路,一個是鐵路。考慮階段為當前到了哪個點。描述一個狀態則需要知道經過了多少公路,多少鐵路。所以,我們設計狀態如下:

\(f[x][l][r]\)表示當前在x點,經過了l條未翻修的公路、r條未翻修的鐵路的最小不方便值。考慮到正着並不好推,我們嘗試着倒着推。每個點只會被兩個子節點更新,且一個子節點是經過公路,一個是經過鐵路。不難得到,我們有如下方程:

\[f[x][l][r]=min(f[ls[x]][l+1][r],f[rs[x]][l][r+1]) \]

當x為葉節點時,由題中的式子,我們可以得到如下初始狀態:

\[f[x][l][r]=c_i*(a_i+l)*(b_i+r) \]

在dfs時,為了優化時間,我們需要記下當前最多會經過多少條沒有翻修的公路和鐵路。但是,這並不能解決空間的問題。可以發現,每次結束遞歸的點此后都不會再調用,所以我們可以每次給一個點一個編號,結束遞歸回收利用編號,就可以做到優化空間了。

代碼

#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long
#define N 40002
using namespace std;
int n,i,a[N],b[N],c[N],ls[N],rs[N],f[102][42][42],dfn[N];
int read()
{
	char c=getchar();
	int w=0,f=1;
	while(c<'0'||c>'9'){
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c<='9'&&c>='0'){
		w=w*10+c-'0';
		c=getchar();
	}
	return w*f;
}
void dfs(int x,int l,int r,int tim)
{
	dfn[x]=tim;
	if(x>=n){
		for(int i=0;i<=l;i++){
			for(int j=0;j<=r;j++) f[dfn[x]][i][j]=c[x]*(a[x]+i)*(b[x]+j);
		}
		return;
	}
	dfs(ls[x],l+1,r,tim+1);
	dfs(rs[x],l,r+1,tim+2);
	int pl=dfn[ls[x]],pr=dfn[rs[x]];
	for(int i=0;i<=l;i++){
		for(int j=0;j<=r;j++){
			f[dfn[x]][i][j]=min(f[pl][i][j]+f[pr][i][j+1],f[pl][i+1][j]+f[pr][i][j]);
		}
	}
}
signed main()
{
	n=read();
	for(i=1;i<=n-1;i++){
		ls[i]=read(),rs[i]=read();
		if(ls[i]<0) ls[i]=-ls[i]+n-1;
		if(rs[i]<0) rs[i]=-rs[i]+n-1;
	}
	for(i=n;i<=2*n-1;i++) a[i]=read(),b[i]=read(),c[i]=read();
	memset(f,0x3f,sizeof(f));
	dfs(1,0,0,1);
	printf("%lld\n",f[1][0][0]);
	return 0;
}

反思

  • 沒有看到路徑長度小於等於40的條件,導致半天沒有設計狀態的思路。
  • 正難則反,老是想着從上到下,沒有想過可以從葉節點出發倒推得到答案。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM