描述
給定一棵含有 n 個結點的樹,結點從 1 標號。你從 1 號結點駕車出發,希望遍歷一些關鍵結點(訪問到就好,不需要按照這些關鍵結點的輸入順序)。每條邊有兩個權值,c0, c1 分別表示步行和駕車經過這條邊的代價。每次你可以選擇駕車經過一條邊(當且僅當有車),或者將車停放在當前所在的結點(如果有車),步行經過一條邊。
求遍歷完所有關鍵結點的最少代價。
注意: 你可以在任意結點結束遍歷,即使當前沒有車。
解題報告:
用時:2h20min,1RE1WA
這題做的不是很好,首先看到題目思考了許久找不到好一點的狀態,然后參考了題解的狀態
\(f[x][0/1/2/3/4]\)表示x子樹內所有的關鍵點都已經走完的5中狀態的最小代價
\(f[x][0]\)表示只考慮人走,且人必須回到x的最小代價
\(f[x][1]\)表示只考慮人走,且人可以不回來的最小代價(用于f[x][4]的轉移)
\(f[x][2]\)表示人車一起走,且兩者都回來的最小代價
\(f[x][3]\)表示人車一起走,且人回車不回的最小代價(用于f[x][4]的轉移)
\(f[x][4]\)表示人車一起走,最后兩者都不回來的最小代價
在自己做的時候并沒有定義到\(f[x][3]\)這個狀態,然后發現\(f[x][4]\)是可以借助\(f[x][3]\)轉移的
設\(dis\)為該邊人走的代價,\(dis0\)為車走的代價,\(u\)為x的子節點
\(f[x][0]=\sum_{u}f[u][0]+2*dis\)
\(f[x][2]=\sum_{u}Min(f[u][2]+2*dis0,f[u][0]+2*dis)\)
這兩個比較顯然,對于\(f[x][2]\)你可以帶著車一起走完回來,也可以把車放在原地,走完回來
設\(t=(f[u][2]+2*dis0,f[u][0]+2*dis)\)
\(f[x][1]\)就是某一個子節點走的是\(f[u][1]+dis\),其他節點走的是\(t\)
\(f[x][3]\)同理,某一個點走\(f[u][3]+dis+dis0\),其他點走\(t\)
顯然這兩個節點的特殊節點的選擇都是選擇貢獻最大的,即\(f[u][1]+dis\)和\(f[u][0]+dis*2\)做差后最大的一個
對于\(f[x][4]\)兩者都不回,我們要分情況討論:
1.在遍歷最后一顆子樹時,有車
顯然遍歷之前是\(f[x][3]\),然后可以選擇最后一顆子樹是開車還是不開車對應\(f[u][1]\)和\(f[u][4]\)
2.若此時沒有車
遍歷之前狀態是\(f[x][2]\),其中某個子樹是\(f[u][3]\),表示車沒有回來人回來了,然后和上面一種情況不同的是:只能選擇人走了,那么就是在不同于選擇了\(f[u][3]\)的子樹中再選擇一個走\(f[v][1]\)
一個我沒注意到的細節:
如果u同于v,那么應該記錄一個次小值,不然就會少一組轉移
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=1e6+5;
int head[N],num=0,to[N<<1],nxt[N<<1],dis[N<<1],dis0[N<<1];
void link(int x,int y,int d,int d0){nxt[++num]=head[x];to[num]=y;dis[num]=d;dis0[num]=d0;head[x]=num;
}
int gi(){int str=0;char ch=getchar();while(ch>'9' || ch<'0')ch=getchar();while(ch>='0' && ch<='9')str=(str<<1)+(str<<3)+ch-48,ch=getchar();return str;
}
int n,m;bool vis[N],mark[N];ll f[N][5];
void dfs(int x,int last){int u,imp;ll t,f1=0,f2=0,f3=0,tmp,f4=0;mark[x]=vis[x];for(int i=head[x];i;i=nxt[i]){u=to[i];if(u==last)continue;dfs(u,x);mark[x]+=mark[u];if(!mark[u])continue;f[x][0]+=f[u][0]+(dis[i]<<1);t=Min(f[u][2]+(dis0[i]<<1),f[u][0]+(dis[i]<<1));f[x][2]+=t;f1=Min(f1,f[u][1]-f[u][0]-dis[i]);tmp=f[u][3]+dis[i]+dis0[i]-t;if(tmp<f2){f4=f2;f2=tmp;imp=u;}else if(tmp<f4)f4=tmp;f3=Min(f3,min(f[u][4]+dis0[i],f[u][1]+dis[i])-t);}f[x][1]=f[x][0]+f1;f[x][3]=f[x][2]+f2;f[x][4]=f[x][2]+f3;f[x][4]=Min(f[x][4],f[x][3]);for(int i=head[x];i;i=nxt[i]){u=to[i];if(u==last || !mark[u])continue;t=Min(f[u][2]+(dis0[i]<<1),f[u][0]+(dis[i]<<1));tmp=f[u][1]+dis[i]-t;if(u==imp)f[x][4]=Min(f[x][4],f[x][2]+tmp+f4);else f[x][4]=Min(f[x][4],f[x][2]+tmp+f2);}
}
void work()
{int x,y,d,d0;n=gi();for(int i=1;i<n;i++){x=gi();y=gi();d=gi();d0=gi();link(x,y,d,d0);link(y,x,d,d0);}m=gi();for(int i=1;i<=m;i++){x=gi();vis[x]=true;}dfs(1,1);printf("%lld\n",Min(f[1][4],f[1][0]));
}int main()
{work();return 0;
}
轉載于:https://www.cnblogs.com/Yuzao/p/7527289.html
總結
以上是生活随笔為你收集整理的hihocoder 1035 : 自驾旅行 III的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。