POJ 2152 Fire 树形dp 压行大法好
题意:在树上建消防站,给出树边的距离,给出每个点建消防站的花费和能覆盖的距离,要求建消防站覆盖整张图,问最小花费是多少。
这题贪心?没法做。暴力枚举搜索,just wash wash and sleep!
我们还是树形dp吧,其实n^2的树形dp我是之前没见过的,怎么搞?这里面蕴藏的东西还是很暴力的。。。
我们这样,先搞出dis[x][y]表示点x到点y的距离!暴力就好。完了之后弄一个dp数组,dp[x][y]表示x点被y点覆盖的情况所得到的最优答案(这里面包括其子树的花费),然后转移时要枚举y判断能覆盖哪个x,对于能覆盖的x,求出y覆盖其子树的最优解,向dp[x][y]转移,最终转移给答案数组。这转移的时候情况要考虑周全,而且不要忘了将算重的花费删掉。
上代码吧。
以上。
//我佩服你沉着机灵有胆量 #include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cmath> #include<queue> #include<cstring> #define ms(a,p) memset(a,p,sizeof(a)) using namespace std; const int MAXN=2010; struct node{ int y,z,nxt; }edge[MAXN*2]; int cost[MAXN],d[MAXN],dis[MAXN][MAXN]; int head[MAXN],dp[MAXN][MAXN],ans[MAXN]; int n,m,k,tot,indx=0,t;queue<int>q; void add(int x,int y,int z){ edge[++indx].y=y;edge[indx].z=z; edge[indx].nxt=head[x];head[x]=indx; } void getdis(int dis[],int s){ dis[s]=0;q.push(s); while(q.size()){ int x=q.front();q.pop(); for(int i=head[x],y;i;i=edge[i].nxt) if(!dis[y=edge[i].y]&&y!=s) dis[y]=dis[x]+edge[i].z,q.push(y); } } void dyna_pro(int x,int fa){ for(int i=head[x],y;i;i=edge[i].nxt) if((y=edge[i].y)!=fa) dyna_pro(y,x); for(int y=1,tmp=0;y<=n;y++,tmp=0) if(dis[x][y]<=d[x]){ for(int i=head[x],k;i;i=edge[i].nxt){ if((k=edge[i].y)!=fa) tmp+=min(dp[k][y]-cost[y],ans[k]); } dp[x][y]=cost[y]+tmp; } else dp[x][y]=1e9; for(int i=1;i<=n;i++) ans[x]=min(ans[x],dp[x][i]); } int main(){ scanf("%d",&t); while(t--){ indx=0;ms(head,0);ms(dp,0);ms(dis,0);ms(ans,0x3f); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&cost[i]); for(int i=1;i<=n;i++) scanf("%d",&d[i]); for(int i=1,x,y,z;i<=n-1;i++){ scanf("%d%d%d",&x,&y,&z); add(x,y,z);add(y,x,z); } for(int i=1;i<=n;i++) getdis(dis[i],i); dyna_pro(1,0);printf("%d\n",ans[1]); } return 0; } //竟敢在尹兄面前耍花枪!