【树形DP】POJ 2152 Fire
通道:http://poj.org/problem?id=2152
题意:n个城市,建防火站,花费w[i],如果这个城市没有防火站,则最近的不超过d[i],求最小花费
思路:
设dp[i][j]表示i点及其子树都符合情况下i点依赖j点的最小花费,有了这个似乎还不够,再开个一维数组best,best[i]表示以i为根的子树符合题目要求的最小花费。这样状态转移方程就是dp[i][j] = cost[j] + sum(min(dp[k][j]-cost[j],best[k])) (k为i的子节点,j为我们枚举的n个点),因为i的每个子节点可以和i一样依赖j结点,那么花费是dp[k][j]-cost[j],或者依赖以k为根的树中的某点,花费是best[k],最后再加上cost[j]
代码:
#include <cstdio> #include <cstring> #include <algorithm> #define N 1010 #define inf 0x3f3f3f3f using namespace std; int n,cost[N],limit[N],tim[N],dep,son[N]; int dis[N][N]; int dp[N][N],best[N]; int head[N],cnt; struct Edge{ int v,w,next; }edge[N*2]; void init(){ memset(head,-1,sizeof(head)); memset(tim,0,sizeof(tim)); cnt=dep=0; } void addedge(int u,int v,int w){ edge[cnt].v=v; edge[cnt].w=w; edge[cnt].next=head[u]; head[u]=cnt++; edge[cnt].v=u; edge[cnt].w=w; edge[cnt].next=head[v]; head[v]=cnt++; } void init_dfs(int u,int fa){ tim[u]=++dep; son[u]=1; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v==fa)continue; init_dfs(v,u); son[u]+=son[v]; } } void cal_dis(int u,int fa,int now){ for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v==fa)continue; dis[now][v]=dis[now][u]+edge[i].w; cal_dis(v,u,now); } } void dfs(int u,int fa){ for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v==fa) continue; dfs(v,u); } best[u]=inf; for(int j=0;j<n;j++){ if(dis[u][j]<=limit[u]){ dp[u][j]=cost[j]; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v==fa) continue; dp[u][j]+=min(best[v],dp[v][j]-cost[j]); } if(tim[j]>=tim[u] && tim[j]<=tim[u]+son[u]-1) best[u]=min(best[u],dp[u][j]); } else dp[u][j]=inf; } } int main(){ int T; scanf("%d",&T); for(int t=0;t<T;t++){ init(); scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d",&cost[i]); for(int i=0;i<n;i++) scanf("%d",&limit[i]); for(int i=0;i<n-1;i++){ int u,v,w; scanf("%d %d %d",&u,&v,&w); u--;v--; addedge(u,v,w); } init_dfs(0,-1); for(int i=0;i<n;i++){ dis[i][i]=0; cal_dis(i,-1,i); } dfs(0,-1); printf("%d\n",best[0]); } }