1. 程式人生 > >Fire (poj 2152 樹形dp)

Fire (poj 2152 樹形dp)

main turn 方程 == 如果 source += float ted

Fire (poj 2152 樹形dp)

給定一棵n個結點的樹(1<n<=1000)。現在要選擇某些點,使得整棵樹都被覆蓋到。當選擇第i個點的時候,可以覆蓋和它距離在d[i]之內的結點,同時花費為v[i]。問最小花費。

以前做過一道類似的題(水庫),這道題也差不多。首先來考慮,用\(best[i]\)表示以i為根的子樹的最小花費。這樣做有什麽問題呢?它無法很好的處理消防站重復建的問題。

所以換一種做法。\(best[i]\)依然表示原來的含義,新建一個數組\(f[i][j]\),表示當i這個結點,依賴j的消防站時的最小花費。轉移方程就是:\(f[i][j]=v[i]+\sum min(f[son_k][j]-v[j], best[son_k])\)

。註意當\(dis(i, j)>d[i]\)時,\(f[i][j]=\infty\)。它的思想就是如果i依賴j,就直接讓子樹中依賴j的點都減去依賴,從而消除影響。

(傻逼了,用rmq求樹上兩點距離)

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn=1005, maxm=1005, logn=12, INF=1e9;

struct Graph{
    struct Edge{
        int to, next, v; Graph *bel;
        inline
int operator*(){ return to; } Edge& operator ++(){ return *this=bel->edge[next]; } }edge[maxm*2]; void reset(){ memset(fir, 0, sizeof(fir)); cntedge=0; } void addedge(int x, int y, int v){ Edge& e=edge[++cntedge]; e.to=y; e.next=fir[x]; e.v=v; e.bel=this
; fir[x]=cntedge; } Edge& getlink(int x){ return edge[fir[x]]; } int cntedge, fir[maxn]; }g; //初始化及樹形dp int T, n, mi[logn], w[maxn], d[maxn]; int best[maxn], dp[maxn][maxn]; //求兩點距離(rmq lca) int id[maxn*2], fir[maxn], dep[maxn], time; int st[maxn*2][logn]; void predfs(int now, int par){ Graph::Edge e=g.getlink(now); id[++time]=now; fir[now]=time; for (; *e; ++e){ if (*e==par) continue; dep[*e]=dep[now]+e.v; predfs(*e, now); id[++time]=now; } } int mindep(int x, int y){ return (dep[x]<dep[y])?x:y; } void init_st(){ for (int i=1; i<=n*2; ++i) st[i][0]=id[i]; for (int i=1; i<logn; ++i) for (int j=1; j<=n*2; ++j) if (j+mi[i]-1<=n*2) st[j][i]=mindep(st[j][i-1], st[j+mi[i-1]][i-1]); } int log2(float x){ return ((unsigned&)x>>23&255)-127; } int getlca(int x, int y){ if (fir[x]>fir[y]) swap(x, y); int fx=fir[x], fy=fir[y]; int logxy=log2(fy-fx+1); return mindep(st[fx][logxy], st[fy-mi[logxy]+1][logxy]); } int dis(int x, int y){ int lca=getlca(x, y); return dep[x]+dep[y]-2*dep[lca]; } void dfs(int now, int par){ Graph::Edge e=g.getlink(now); for (; *e; ++e) if (*e!=par) dfs(*e, now); e=g.getlink(now); best[now]=INF; for (int j=1; j<=n; ++j) dp[now][j]=(dis(now, j)<=d[now]?w[j]:INF); for (; *e; ++e) if (*e!=par) for (int j=1; j<=n; ++j) if (dis(now, j)<=d[now]) dp[now][j]+=min(dp[*e][j]-w[j], best[*e]); for (int j=1; j<=n; ++j) best[now]=min(best[now], dp[now][j]); } int main(){ scanf("%d", &T); int x, y, l; mi[0]=1; for (int i=1; i<logn; ++i) mi[i]=mi[i-1]*2; while (T--){ g.reset(); scanf("%d", &n); for (int i=1; i<=n; ++i) scanf("%d", &w[i]); for (int i=1; i<=n; ++i) scanf("%d", &d[i]); for (int i=1; i<n; ++i){ scanf("%d%d%d", &x, &y, &l); g.addedge(x, y, l); g.addedge(y, x, l); } time=0; predfs(1, 0); dep[1]=0; init_st(); dfs(1, 0); printf("%d\n", best[1]); } return 0; }

Fire (poj 2152 樹形dp)