题意:
给定一张有向无环图,每个节点视作一个路口,每条边视作路,要求挑选一些节点放置路灯,使每条路都能被路灯照到,且使用的路灯数最少,如若存在使用相同路灯数的情况,则使得能被两盏路灯照到的路的数量尽量多。
解题:
可以将此问题提炼一下,就是使用最少的路灯照亮所有的路,使得被两盏路灯照亮的路尽量多,也就是使被一盏路灯照亮的路尽量少。那么问题可以转换为,使用最少x盏路灯,使得最少为y条路被一盏路灯照亮。那么问题就抽象为,W=k*x+y(其中k>y)使得W尽量小。因为,k>y,也就保障了x为首要条件。dp[i][0]表示第i个节点不选,dp[i][1]表示第i个节点选的最小值。当一个节点其父节点为不选时,那么它必选,因为若不选,那么他们之间的路就无法照亮了,倘若父节点选了,那么该节点可选可不选,取两者间的小者,若相同,那么则选选的,因为这样被两盏灯照亮的路径数多。
代码:
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <cstdio>
#include <cstring>
#include <map>
#define inf 0x3f3f3f3f
using namespace std;
int n,m,head[1010],cnt,nxt[2020],dp[1010][2];
bool vis[1010];
struct edge
{
int fm,to;
}store[2020];
int min(int a,int b)
{
return a<b?a:b;
}
void add_edge(int x,int y)
{
nxt[cnt]=head[x];
head[x]=cnt;
store[cnt].fm=x;
store[cnt].to=y;
cnt++;
nxt[cnt]=head[y];
head[y]=cnt;
store[cnt].fm=y;
store[cnt].to=x;
cnt++;
}
void dfs(int x)
{
int to;
vis[x]=1;
dp[x][0]=0;
dp[x][1]=1010;
for(int i=head[x];~i;i=nxt[i])
{
to=store[i].to;
if(vis[to])continue;
dfs(to);
dp[x][0]+=dp[to][1]+1;//一盏灯照亮
if(dp[to][0]<dp[to][1])
dp[x][1]+=dp[to][0]+1; //单盏灯亮的情况
else
dp[x][1]+=dp[to][1]; //相等的情况下,选择不加1
}
}
int main()
{
int t,a,b,ans;
scanf("%d",&t);
while(t--)
{
cnt=ans=0;
scanf("%d%d",&n,&m);
memset(head,-1,sizeof(head));
memset(vis,0,sizeof(vis));
memset(nxt,-1,sizeof(nxt));
for(int i=0;i<m;i++)
{
scanf("%d%d",&a,&b);
add_edge(a,b);
}
for(int i=0;i<n;i++)
{
if(!vis[i])
{
dfs(i);
ans+=min(dp[i][0],dp[i][1]);
}
}
printf("%d %d %d\n",ans/1010,m-ans%1010,ans%1010);
}
return 0;
}