HDU 4313 Matrix 贪心 || 树形dp

题意:给定n(n<=100000)个节点的树,每条边有一定的权值,有m个点是危险的,现在想将树分成m块使得每块中恰好只有一个危险的点,问最小的花费是多少。

题解:1贪心:类似Kruskal的贪心过程,以每个危险的节点为并查集的根节点,附代码不赘述。

         2树形dp:dp[i][0]代表的是在当前以i节点为根节点的子树中,i所在的连通块中没有危险节点的最小花费;
                         dp[i][1]代表的是在当前以i节点为根节点的子树中,i所在的连通块中有危险节点的最小花费;

                         如果i是叶子节点:如果i为危险点dp[i][0] = inf,dp[i][1]= 0;否则dp[i][0] = dp[i][1] = 0;

                         如果i不是叶子节点:如果i是危险点dp[i][0] = inf , dp[i][1] = sigma min(dp[son][0],dp[son][1]+w);
                         否则dp[i][0] = sigma min(dp[son][0],dp[son][1]+w),dp[i][1] = min(dp[i][0] – min(dp[son][0],dp[son][1]+w)+dp[son][1])。

Sure原创,转载请注明出处。
贪心:

#include <iostream>
#include <cstdio>
#include <memory.h>
#include <algorithm>
using namespace std;
const int maxn = 100002;
struct node
{
    int u,v;
    __int64 w;
    bool operator < (const node &other) const
    {
        return w > other.w;
    }
}edge[maxn];
int father[maxn];
bool machine[maxn];
int m,n;

void init()
{
    memset(machine,false,sizeof(machine));
    scanf("%d %d",&n,&m);
    for(int i=0;i<n;i++)
    {
        father[i] = i;
    }
    return;
}

int find(int u)
{
    return (u == father[u]) ? father[u] : (father[u] = find(father[u]));
}

void read()
{
    for(int i=0;i<n-1;i++)
    {
        scanf("%d %d %I64d",&edge[i].u,&edge[i].v,&edge[i].w);
    }
    int u;
    for(int i=0;i<m;i++)
    {
        scanf("%d",&u);
        machine[u] = true;
    }
    return;
}

void solve()
{
    __int64 sum = 0;
    sort(edge,edge+n-1);
    for(int i=0;i<n-1;i++)
    {
        int x = find(edge[i].u);
        int y = find(edge[i].v);
        if(machine[x] && machine[y])
        {
            sum += edge[i].w;
        }
        else if(machine[x])
        {
            father[y] = x;
        }
        else
        {
            father[x] = y;
        }
    }
    printf("%I64d\n",sum);
    return;
}

int main()
{
    int cas;
    scanf("%d",&cas);
    while(cas--)
    {
        init();
        read();
        solve();
    }
    return 0;
}

树形dp:

#include <iostream>
#include <cstdio>
#include <memory.h>
#define MIN(a , b) ((a) < (b) ? (a) : (b))
using namespace std;
const __int64 inf = 1LL << 45;
const int maxn = 100002;
struct node
{
    int v;
    __int64 w;
    int next;
}edge[maxn << 1];
int head[maxn];
__int64 dp[maxn][2];
bool machine[maxn];
int n,m,idx;

void init()
{
    memset(head,-1,sizeof(head));
    memset(machine,false,sizeof(machine));
    idx = 0;
    return;
}

void addedge(int u,int v,__int64 w)
{
    edge[idx].v = v;
    edge[idx].w = w;
    edge[idx].next = head[u];
    head[u] = idx++;

    edge[idx].v = u;
    edge[idx].w = w;
    edge[idx].next = head[v];
    head[v] = idx++;
    return;
}


void read()
{
    scanf("%d %d",&n,&m);
    int u,v;
    __int64 w;
    for(int i=1;i<n;i++)
    {
        scanf("%d %d %I64d",&u,&v,&w);
        addedge(u,v,w);
    }
    for(int i=0;i<m;i++)
    {
        scanf("%d",&u);
        machine[u] = true;
    }
    return;
}

void dfs(int st,int pre)
{
    bool leaf = true;
    for(int i=head[st];i != -1;i=edge[i].next)
    {
        if(edge[i].v == pre) continue;
        leaf = false;
        dfs(edge[i].v , st);
    }
    if(leaf)
    {
        dp[st][0] = machine[st] ? inf : 0;
        dp[st][1] = 0;
        return;
    }
    if(machine[st])
    {
        dp[st][0] = inf;
        dp[st][1] = 0;
        for(int i=head[st];i != -1;i=edge[i].next)
        {
            if(edge[i].v == pre) continue;
            dp[st][1] += MIN(dp[edge[i].v][0] , edge[i].w + dp[edge[i].v][1]);
        }
    }
    else
    {
        dp[st][0] = 0;
        dp[st][1] = inf;
        for(int i=head[st];i != -1;i=edge[i].next)
        {
            if(edge[i].v == pre) continue;
            dp[st][0] += MIN(dp[edge[i].v][0] , edge[i].w + dp[edge[i].v][1]);
        }
        for(int i=head[st];i != -1;i=edge[i].next)
        {
            if(edge[i].v == pre) continue;
            dp[st][1] = MIN(dp[st][1] , dp[st][0] - MIN(dp[edge[i].v][0] , edge[i].w + dp[edge[i].v][1]) + dp[edge[i].v][1]);
        }
    }
    return;
}

int main()
{
    int cas;
    scanf("%d",&cas);
    while(cas--)
    {
        init();
        read();;
        dfs(0,-1);
        printf("%I64d\n",MIN(dp[0][0] , dp[0][1]));
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值