hdu 2152 fire

/*
题目链接:http://poj.org/problem?id=2152

  题意:城市树形分布,每个城市修建消防站的有一个花费值cost,且,当该地着火,离他最近的那个消防站必须小于
  need,问要保证每个地方都能得到安全防护,需要的最小花费为?
 
    解题思路:令dp[i][j]为,保护i点的消防站修建在j点且以i为根节点,所有孩子即自己都能得到保护的的最小花费
    状态转移方程:dp[i][j]=w[j]+sum(min(dp[child][j]-w[j],best[child]));
    
      转载:
      
        非常非常好的一道树状DP!
        问题描述:
        Z国有n个城市,从1到n给这些城市编号。城市之间连着高速公路,并且每两个城市之间有且只有一条通路。不同的高速公路可能有不同的长度。最近Z国经常发生火灾,所以当地政府决定在某些城市修建一些消防站。在城市k修建一个消防站须要花费大小为的费用。函数W对于不同的城市可能有不同的取值。如果在城市k没有消防站,那么它到离它最近的消防站的距离不能超过。每个城市在不超过距离的前提下,必须选择最近的消防站作为负责站。函数D对于不同的城市可能有不同的取值。为了节省钱,当地政府希望你用最少的总费用修建一些消防站,并且使得这些消防站满足上述的要求。
        设:
        f[i][j]:表示以i为根的子树里修建一些消防站,并在节点j处修建一消防站,i的负责站必须是j
        best[i]:表示以i为根的子树的所有节点都有负责站的最小花费
        dist[i]表示i到x的距离,所以没换一个节点,都要再求一次dist[]。
        好了下面说下转移方程:
        首先选定一个节点(x)为根 ,遍历所有节点
        如果d[x]<dist[i],则i点不可能成为x的负责站f[x][i]=INF;
        如果d[x]>=dist[i],则i可以成为x的负责站,分三种情况讨论:(y是x的儿子)
        A.
        当i在以x为根的子树外时,对于x的每个儿子y都有两种选择:选择以y为根的子树内或外的消防站为负责站。当选择以y为根的子树内的消防站为负责站时,其子树所需的最少费用为best[y],当选择以y为根的子树外的消防站为负责站时,y只可以选择i上的消防站作为负责站,此时其子树所需的最少费用为f[k][i]。综上得到
        f[x][i]+=min(best[y],f[y][i]);
        (其实这块是反过来想的,当i点在x为根的子树外时,如果的x的儿子y选择i作为负责站,那么在i节点与y节点中间的x就一定是选择i了。)
        B.
        当x==i时,x的每个儿子的选择情况与A中的一样。此时还要加上修建x上的消防站的费用。因此
        f[x][i]+=w[x]+min(best[y],f[y][i]);
        C.
        当x!=i并且i在以x为根的子树内时,此时i必定在x的某个儿子y的子树里。对于x的每个不是y的儿子其选择情况与⑴中的一样,而对于y,它只能选择j作为负责站。综上得到
        f[x][i]+=f[y][i]+min(best[k],f[k][i]);(k是x中不是y的所有儿子)
*/
#include<stdio.h>
#include<string.h>
#define MAXN 1005
#define MAXE 2005
#define INF 100000000
#define min(a,b) ((a<b)?a:b)
int head[MAXN],cost[MAXN],need[MAXN],dis[MAXN];
struct Edge{
    int node;
    int next;
    int len;
}edge[MAXE];
int cnt,N;
int dp[MAXN][MAXN],ans[MAXN];
void add(int a,int b,int c)
{
    edge[cnt].len=c;edge[cnt].next=head[a];edge[cnt].node=b;head[a]=cnt++;
    edge[cnt].len=c;edge[cnt].next=head[b];edge[cnt].node=a;head[b]=cnt++;
}
void dfs(int fa,int root,int sum)
{
    int i;
    dis[root]=sum;
    for(i=head[root];~i;i=edge[i].next)
        if(edge[i].node!=fa)
            dfs(root,edge[i].node,sum+edge[i].len);    
}
void DP(int fa,int root)
{
    int i,j;
    for(i=head[root];~i;i=edge[i].next)
    {
        if(edge[i].node!=fa)
            DP(root,edge[i].node);    
    }
    dfs(-1,root,0);
    ans[root]=INF;
    for(i=1;i<=N;i++)
    {
        if(dis[i]<=need[root])
        {
            dp[root][i]=cost[i];
            for(j=head[root];~j;j=edge[j].next)
            {
                if(edge[j].node!=fa)
                {
                    dp[root][i]+=min(ans[edge[j].node],dp[edge[j].node][i]-cost[i]);
                }
            }
            if(dp[root][i]<ans[root])ans[root]=dp[root][i];
        }
        else
            dp[root][i]=INF;
    }
}
int main()
{
    int T,i,a,b,c;
    //    freopen("in.txt","r",stdin);
    for(scanf("%d",&T);T--;)
    {
        scanf("%d",&N);
        for(i=1;i<=N;i++)scanf("%d",&cost[i]);
        for(i=1;i<=N;i++)scanf("%d",&need[i]);
        
        memset(head,-1,sizeof(head));
        for(i=1,cnt=0;i<N;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            add(a,b,c);
        }
        DP(-1,1);
        printf("%d\n",ans[1]);
    }
    return 0;
}

/*
10
5
1 1 1 1 1
1 1 1 1 1
1 2 1
2 3 1
3 4 1
4 5 1
5
1 1 1 1 1
2 1 1 1 2
1 2 1
2 3 1
3 4 1
4 5 1
5
1 1 3 1 1
2 1 1 1 2
1 2 1
2 3 1
3 4 1
4 5 1
4
2 1 1 1
3 4 3 2
1 2 3
1 3 3
1 4 2
4
4 1 1 1
3 4 3 2
1 2 3
1 3 3
1 4 2

*/


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值