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;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值