bzoj1040 一群骑士,每个人都有战斗力和一个讨厌的人,要求选出一系列骑士,被选中的人不能与讨厌的人同时选中,求战斗力之和的最大值。
显然厌恶关系构成的图是基环树森林,如果没有环那么就是简单的树形dp比如没有上司的舞会……对于每一棵树或者基环树,如果是树,就是树形dp;如果是基环树,就用dfs找环,在环中任意取一条边,把这条边在图中删去构成一棵树。然而因为这条边仍然是真实存在的,就对于这条边的两个顶点,分别以这两个点为根,做一次不取这个点的树形dp。dp[i][0/1]表示以i为根的子树中取/不取i的最大战斗力。
/*ргргрг*/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1000050;
const int INF = 0x3f3f3f3f;
const ll mod = 1000000007;
const double eps = 1e-8;
int n, x, no, tp1, tp2, ban;
bool vis[maxn];
int head[maxn];
ll w[maxn], dp[maxn][2], ans;
struct node
{
int to;
int nxt;
}e[maxn << 1];
void add(int a, int b)
{
e[no].to = b;
e[no].nxt = head[a];
head[a] = no++;
}
void sea_ring(int u, int fa)
{
vis[u] = 1;
for(int i = head[u];i != -1;i = e[i].nxt)
{
int v = e[i].to;
if(v == fa) continue;
if(vis[v])
{
ban = i;
tp1 = u, tp2 = v;
}
else sea_ring(v, u);
}
}
void dfs(int u, int fa)
{
dp[u][0] = 0, dp[u][1] = w[u];
for(int i = head[u];i != -1;i = e[i].nxt)
{
int v = e[i].to;
if(v == fa) continue;
if(i == ban || i == (ban ^ 1)) continue;
dfs(v, u);
dp[u][0] += max(dp[v][0], dp[v][1]);
dp[u][1] += dp[v][0];
}
}
int main()
{
scanf("%d", &n);
no = 0;
memset(head, -1, sizeof(head));
for(int i = 1;i <= n;i++)
{
scanf("%lld%d", &w[i], &x);
add(i, x);
add(x, i);
}
ans = 0;
memset(vis, 0, sizeof(vis));
for(int i = 1;i <= n;i++)
{
if(vis[i]) continue;
sea_ring(i, -1);
dfs(tp1, -1);
ll tmp = dp[tp1][0];
dfs(tp2, -1);
ans += max(tmp, dp[tp2][0]);
}
printf("%lld\n", ans);
return 0;
}
poj2152 有一些城市构成了树形结构,现在需要在某些城市修建消防站,在第i个城市修建消防站需要wi的费用,同时第i个城市可以被距离不超过di的消防站覆盖。现要求所有的城市都被消防站覆盖,求最小的总费用。
由于是树形结构,可以在n^2的时间内预处理出图中各点之间的距离,用dis(i, j)表示图中i和j之间的距离。
考虑在树上dp,ans[i]表示以i为根的子树的结果,dp[u][v]表示u被v覆盖时,以u为根的子树的结果。对于每一个dis(u, v)<=d(u)的节点v,都尝试用v去覆盖u。此时对于u的每一个子节点k,都考虑被v覆盖或不被v覆盖的情况,在这两种情况中取较小值加入到u的答案中。如果dis(u, v)>d(u)那么dp[u][v]就设成INF。即状态转移方程为:
显然有由于对u转移的过程中用到了u的子节点的结果,在dp的时候需要先递归,在回溯的时候进行转移。
/*ргргрг*/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1005;
const int INF = 0x3f3f3f3f;
const ll mod = 1000000007;
const double eps = 1e-8;
int t, n, no, a, b, x, ans[maxn];
int dis[maxn][maxn], dp[maxn][maxn];
int w[maxn], d[maxn], head[maxn];
bool vis[maxn];
struct node
{
int to;
int nxt;
int w;
}e[maxn << 1];
void add(int a, int b, int x)
{
e[no].to = b;
e[no].w = x;
e[no].nxt = head[a];
head[a] = no++;
}
void cal_dis(int s)
{
queue<int>q;
memset(vis, 0, sizeof(vis));
vis[s] = 1;
q.push(s);
while(!q.empty())
{
int u = q.front();
q.pop();
for(int i = head[u];i != -1;i = e[i].nxt)
{
int v = e[i].to;
if(!vis[v])
{
dis[s][v] = dis[s][u] + e[i].w;
vis[v] = 1;
q.push(v);
}
}
}
}
void init()
{
no = 0;
memset(head, -1, sizeof(head));
memset(dp, 0, sizeof(dp));
memset(dis, 0, sizeof(dis));
for(int i = 1;i <= n;i++)
ans[i] = INF;
}
void dfs(int u, int fa)
{
vis[u] = 1;
for(int i = head[u];i != -1;i = e[i].nxt)
{
int v = e[i].to;
if(!vis[v]) dfs(v, u);
}
for(int v = 1;v <= n;v++)
{
if(dis[u][v] <= d[u])
{
int tmp = 0;
for(int i = head[u];i != -1;i = e[i].nxt)
{
int k = e[i].to;
if(k == fa) continue;
tmp += min(ans[k], dp[k][v] - w[v]);
}
dp[u][v] = tmp + w[v];
}
else dp[u][v] = INF;
}
for(int i = 1;i <= n;i++)
ans[u] = min(ans[u], dp[u][i]);
}
int main()
{
scanf("%d", &t);
while(t--)
{
scanf("%d", &n);
init();
for(int i = 1;i <= n;i++) scanf("%d", &w[i]);
for(int i = 1;i <= n;i++) scanf("%d", &d[i]);
for(int i = 1;i < n;i++)
{
scanf("%d%d%d", &a, &b, &x);
add(a, b, x);
add(b, a, x);
}
for(int i = 1;i <= n;i++) cal_dis(i);
memset(vis, 0, sizeof(vis));
dfs(1, 0);
printf("%d\n", ans[1]);
}
return 0;
}