题目连接:https://vjudge.net/problem/UVA-10859
题意:给出一个无向无环图,n个点,m条边,每再一个端点放置一个灯可以照亮相邻的边,求在放置最少灯且照亮所有边的情况下,被两盏灯照亮的边数应该尽量大。
思路:本题转化为在求放置灯最少的情况下,一盏灯覆盖的边应该尽量小,则转化为x=aM+c,a为灯的数量,c为一盏灯覆盖的边数,M为一个大常数,如2000.这样转化为求x尽量小
把它分成子树,dp[i][j] i表示当前节点,j为0或1,表示当前节点的父亲是否有放置灯,0为无。
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<vector>
using namespace std;
vector<int>edge[1100];
int n,m;
int vis[1100][2],dp[1100][2];
int dfs(int i,int j,int f)
{
if(vis[i][j]) return dp[i][j];
vis[i][j]=1;
int &ans=dp[i][j];
ans=2000;
for(int k=0;k<edge[i].size();k++)
{
if(edge[i][k]!=f) ans+=dfs(edge[i][k],1,i);//父亲节点不用算
}
if(!j&&f>=0) ans++;//如果父亲节点没有放在灯,且不是根节点
if(j==1||f<0)
{
int sum=0;
for(int k=0;k<edge[i].size();k++)
{
if(edge[i][k]!=f) sum+=dfs(edge[i][k],0,i);
}
if(f>=0) sum++;//如果不是根节点
ans=min(ans,sum);
}
return ans;
}
int main()
{
int u,v,t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++) edge[i].clear();
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
edge[u].push_back(v);
edge[v].push_back(u);
}
memset(vis,0,sizeof(vis));
int ans=0;
for(int i=1;i<=n;i++)
{
if(!vis[i][0]) ans+=dfs(i,0,-1);
}
printf("%d %d %d\n",ans/2000,m-ans%2000,ans%2000);
}
}
白书70页