【JZOJ A組】傳送門
阿新 • • 發佈:2018-11-08
Description
8102年,Normalgod在GLaDOS的幫助下,研製出了傳送槍。但GLaDOS想把傳送槍據為己有,於是把Normalgod扔進了一間實驗室。這間實驗室是一棵有n個節點的樹。現在Normalgod在一號節點,出口也在一號節點,但為了開啟它,必須經過每一個節點按下每個節點的開關,出口才能開啟。GLaDOS為了殺死Normalgod,開始在實驗室裡釋放毒氣,因此Normalgod必須儘快逃出這間實驗室。 當然,Normalgod手中的傳送槍是可以使用的。傳送槍可以發射出兩個顏色不同的傳送門。Normalgod可以從其中一個傳送到另一個。儘管傳送槍可以在視野範圍內的任何一個經過特殊處理的表面開啟一扇傳送門,但這間實驗室的設計使得Normalgod只能在他所處的房間內開啟一個傳送門。 在已經存在了一個同顏色的傳送門時,開啟新的傳送門會使與它同顏色的舊門消失。傳送和開啟傳送門所需時間為0。 顯然,利用傳送槍會讓Normalgod更快解決謎題,可Normalgod死在了按下最後一個按鈕的路上。儘管如此,GLaDOS還是很想知道到底Normalgod最快能用多久逃出去,這對她的實驗室設計方法論有重要的指導作用。作為GLaDOS的演算法模組,你要完成這個任務。本題時限為2000ms
Input
第一行一個整數n。之後n-1行,每行三個整數ui,vi,ai ,表示有一條從ui 連向vi ,花費時間為ai 的通道。
Output
一行一個數T,表示最小的脫逃時間。
Sample Input
5
1 2 2
2 3 3
2 4 5
1 5 1
Sample Output
13
樣例說明
1–> open1–> 5–> open2–> use(1)–> 2–> 3–> open2–> use(1)–> 2–> 4–> open2–> use(1)–> exit
Data Constraint
思路
題意:給定n個結點的樹,每條邊權wi,可以進行傳送門操作,求經過每個結點並最終回到根節點的最短路徑長度。
- 易知:每條邊至多經過2次。
- 2.那麼最長距離總權值2,那麼我們只需要使經過一次的邊的總和更大,則長度=總權值2-選用的單邊權值之和。
設dp[i][0/1]
0表示在i點及i點兒子設傳送門所能得到的最大總和
1 表示不在i點及i點兒子設傳送門所能得到的最大總和
首先,對於dp[i][1]的情況,一定存在i點的祖先中有傳送門,這樣才能使結果更優。所以對於他的每一個兒子都能跳回到他的祖先。但實際上只有使一個兒子跳回祖先時,才能保證fa->i的邊經過兩次。
否則轉化為dp[i][0]的情況。則我們要使選的兒子最優。
dp[i][1]=max(dp[vi][1]+wi);
其次,對於dp[i][0]的情況,在每個兒子中設立傳送門並不會影響到其他兒子,因為總能從兒子回到i時再在i重設傳送門。所以我們就取每個兒子設與不設的最優值之和。
dp[i][0]=Σmax(dp[vi][0],dp[vi][1]+wi);
程式碼
#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
const ll maxn=1e6+77;
long long n,f[maxn][2],d[maxn],list[maxn],cnt;
struct E
{
ll to,next,v;
}e[maxn*2];
void add(ll u,ll v,ll val)
{
e[++cnt].to=v; e[cnt].next=list[u]; list[u]=cnt; e[cnt].v=val;
}
void dfs(ll u,ll fa)
{
for(ll i=list[u]; i; i=e[i].next)
{
ll v=e[i].to; if(v==fa) continue;
dfs(v,u);
d[u]=max(d[u],d[v]+e[i].v);
f[u][0]+=f[v][0]+e[i].v*2; f[u][1]+=min(f[v][0]+e[i].v-d[v],f[v][1]+e[i].v*2);
}
}
int main()
{
freopen("portal.in","r",stdin),freopen("portal.out","w",stdout);
scanf("%lld",&n);
for(ll i=1,x,y,z; i<=n-1; i++) scanf("%lld%lld%lld",&x,&y,&z),add(x,y,z),add(y,x,z);
dfs(1,0);
printf("%lld",f[1][1]);
}