题意:给定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;
}