题意:
给定一个无向,无环,无多重边,要求找出最少的若干点,使得,每条边之中至少有一个点上有街灯。在满足上述条件的时候将还需要满足让两个点被选择的边的数量尽量多。
题解:
对于如何求解最小的节点数目这点,实际上有些想法,“是不是应当认为来一发BFS,之后强行染色招最小的会好呢?”但是这样就会很容易发现一个误区——染色方案被强行确定了。。而这个其实是不能够直接确定为两种的。
因而,应当使用树形dp。应当想将整棵树转化为一个有根树。在转换的过程当中进行动态规划:
对于每个节点,首先都必然可以选择在该点处放一个灯——虽然会导致预算报表。对于不放灯的选项却有个调价——他的父亲节点必须放了灯——否则就会有一条路没有被照亮。。
但是考虑到多目标情况——让同时被两个端点照亮的路尽量多实际上等效于——让植被一个接等找到的路的数量足够小。那么我们可以定义一个优化目标,设他为KK=a*m+b;其中A代表了街灯的数量,M是一个超级大数,该大数至少满足使得M大于 b所能够渠道的最大值。
于是,这样就变成了一个优化变量。。
#include<bits/stdc++.h>
using namespace std; const long long MAXN=;
const long long INF=;
const long long K=;
bool legal[MAXN][];
long long cnt[MAXN][];
bool vis[MAXN]; vector<int> G[MAXN];
long long ans=;
long long n,m;
long long dfs(int self,int father)
{
vis[self]=;
if(self!=father)
{ legal[self][]=;
legal[self][]=legal[father][];
}
int len=G[self].size();
for(int i=;i<len;++i)
{
if(G[self][i]!=father)dfs(G[self][i],self);
} long long ret1=K;
long long ret2=; len=G[self].size();
for(int i=;i<len;++i)
{ int tar=G[self][i];
if(tar==father)continue; if(cnt[tar][]<cnt[tar][])
{
ret1+=cnt[tar][]+;
}else ret1+=cnt[tar][];
// ret1+=min(cnt[tar][1],cnt[tar][0]);
ret2+=cnt[tar][]+; }
if(legal[self][])cnt[self][]=ret2;
else cnt[self][]=INF;
cnt[self][]=ret1; return min(ret1,ret2);
} void init()
{
memset(vis,,sizeof(vis));
memset(cnt,,sizeof(cnt));
memset(legal,,sizeof(legal));
ans=;
cin>>n>>m;
for(int i=;i<m;++i)
{
int a,b;
cin>>a>>b;
G[a].push_back(b);
G[b].push_back(a);
}
for(int i=;i<n;++i)
{
if(!vis[i])
{
legal[i][]=legal[i][]=;
ans+=dfs(i,i);
}
G[i].clear();
}
cout<< ans/K <<" "<<m-ans%K<<" "<<ans%K<<endl;
} int main()
{
cin.sync_with_stdio(false);
int tt;cin>>tt;
while(tt--)init(); return ;
}