[UVA10859]Placing Lampposts

https://zybuluo.com/ysner/note/1248929

题面

给定一个(n)个点(m)条边的无向无环图,在尽量少的节点上放灯,使得所有边都与灯相邻(被灯照亮)。
在灯的总数最小的前提下,被两盏灯同时照亮的边数应该尽可能大。

  • (n,mleq1000)

题面

有一种套路,如果要同时最小化或最大化两个量(a,b),则等价于最小化或最大化(aM+b)
并且,(M)必须大到足以区分(a,b)。一般来说,(M>max{abs(a-b)})
所以本题可以设(M=3000)

但是,题目要求是最小化(a),最大化(b)???
可以转化一下,把(b)表示为“只被一盏灯照亮的边数”(因为(b'=m-b))。

于是设(f[i][0/1])分别表示以(i)为根的子树内,不放灯和放灯对答案((aM+b))的贡献。
转移时不统计被两盏灯同时照亮的边的贡献。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#define re register
#define il inline
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define fp(i,a,b) for(re int i=a;i<=b;i++)
#define fq(i,a,b) for(re int i=a;i>=b;i--)
using namespace std;
const int mod=1e9+7,N=2000,M=3000;
struct Edge{int to,nxt;}e[N<<1];
int n,m,h[N],cnt,dp[N][2],vis[N];
il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;}
ll ans=0;
il ll gi()
{
   re ll x=0,t=1;
   re char ch=getchar();
   while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
   if(ch=='-') t=-1,ch=getchar();
   while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
   return x*t;
}
il void dfs(re int u,re int fa)
{
  vis[u]=1;dp[u][0]=0;dp[u][1]=M;
  for(re int i=h[u];i+1;i=e[i].nxt)
    {
      re int v=e[i].to;
      if(v==fa||vis[v]) continue;
      dfs(v,u);
      dp[u][0]+=dp[v][1]+1;
      dp[u][1]+=min(dp[v][0]+1,dp[v][1]);
    }
}
int main()
{
  re int T=gi();
  while(T--)
    {
      memset(h,-1,sizeof(h));cnt=0;
      n=gi();m=gi();ans=0;
      memset(vis,0,sizeof(vis));
      fp(i,1,m)
    {
      re int u=gi()+1,v=gi()+1;
      add(u,v);add(v,u);
    }
      fp(i,1,n)
    if(!vis[i]) dfs(i,0),ans+=min(dp[i][0],dp[i][1]);
      printf("%lld %lld %lld
",ans/M,m-ans%M,ans%M);
    }
  return 0;
}
原文地址:https://www.cnblogs.com/yanshannan/p/9471926.html