UVA10859 Placing Lampposts --- 树形dp
作者:互联网
题意: 给定n点m边无向无环图,n<1000,在点上放置灯,一条边的至少一个端点有灯,则称该边被一点覆盖,
要求在满足所有边被覆盖的前提下,
要求使用尽量少的灯,
在灯数量最少的情况下,
再要求被2点覆盖的边最多,
求此时的灯数量,被2点覆盖的边数,1点覆盖的边数
题解: 参考 https://www.cnblogs.com/yohanlong/p/7764014.html
解决此题首先需要知道一个结论:
设a>0,b>0, 要求a取最小时,b也取最小值,相当于同时求2个最小,但是a优先级高,则令C = M*a+b, M取一个略微超出b上限的值即可,这样求C得最小值,就等价于求出a,b的最小值。
此题是无向无环图,也就是一个森林,森林的各个树可以用树形dp,因为需要2个最小,所以把题目转为求被1点覆盖的边最少即可
转移方程为 dp[i][0] = sum(dp[son][1]+1) (dp数组第2维表示是否放置灯)
dp[i][1] = sum(min(dp[son][0]+1.dp[son][1])) + M
// ans = M*a+b
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define MAXN 1010
#define M 2333
using namespace std;
int dp[MAXN][2],ans;
vector<int> mp[MAXN];
int n,m;
bool used[MAXN];
// u: current node
// f: father node
// p: place lamp or not
void DFS(int u,int f,int p) {
// memorized search
if(dp[u][p]) return;
used[u] = true;
int size = mp[u].size();
if(p == 0) {
int t = 0;
for(int i = 0;i < size;i++) {
int v = mp[u][i];
if(v == f) continue;
DFS(v,u,1);
t += dp[v][1] + 1;
}
dp[u][0] = t;
} else {
int t = 0;
for(int i = 0;i < size;i++) {
int v = mp[u][i];
if(v == f) continue;
DFS(v,u,0);
DFS(v,u,1);
t += min(dp[v][0]+1,dp[v][1]);
}
t += M;
dp[u][1] = t;
}
}
int main() {
int T;
scanf("%d",&T);
while(T--) {
ans = 0;
for(int i = 0;i < MAXN;i++) mp[i].clear();
memset(dp,0,sizeof(dp));
memset(used,0,sizeof(used));
scanf("%d%d",&n,&m);
for(int i = 0;i < m;i++) {
int a,b;
scanf("%d%d",&a,&b);
mp[a].push_back(b);
mp[b].push_back(a);
}
for(int i = 0;i < n;i++) {
if(!used[i]) {
DFS(i,-1,0);
DFS(i,-1,1);
ans += min(dp[i][0], dp[i][1]);
}
}
printf("%d %d %d\n",ans/M,m-ans%M,ans%M);
}
return 0;
}
标签:int,UVA10859,Placing,DFS,++,mp,ans,dp 来源: https://blog.csdn.net/lmhlmh_/article/details/97668713