题意:
给定一张有向无环图,每个节点视作一个路口,每条边视作路,要求挑选一些节点放置路灯,使每条路都能被路灯照到,且使用的路灯数最少,如若存在使用相同路灯数的情况,则使得能被两盏路灯照到的路的数量尽量多。
解题:
可以将此问题提炼一下,就是使用最少的路灯照亮所有的路,使得被两盏路灯照亮的路尽量多,也就是使被一盏路灯照亮的路尽量少。那么问题可以转换为,使用最少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; }
时间: 2024-11-08 13:45:49