UVA 10859 Placing Lampposts 树形DP
题意:
给一个n个点m条边的无向无环图,再尽量少的结点上放上灯,使得所有边都被照亮(每个灯会照亮所有与它相连的边)
在灯的总数最小的前提下,被两盏灯照亮的边尽量大。
优化条件有两个:灯的数量a最少,被两灯照亮的边数b最大,因为边的总数是一定的,不是被一个灯照亮就是被两个灯照亮
所以第二个条件可用"被一个灯照亮的边数 c最小"代替
设x=a*M+c;M是一个足够大的常数
那么优化条件就变成只有x尽量小了,决定x大小的主要是a的值,在a的值相同的情况下才考虑c的值
最终求到x后,a=x/M,c=x%M,b=m-c;
dp[i][j]表示以结点i为跟的子树的最优解,j为父节点的放灯状态,j=0表示不放,1表示放。
则在i结点的时候有放和不放两种决策
vector<int> g[1010]; int n,m; int dp[1001][2]; int f(int x,int st,int fa) { if(dp[x][st]>=0)return dp[x][st]; int &ans=dp[x][st]; ans=2000; //在这个结点放上等的决策,任何时候都是可行的 for(int i=0;i<g[x].size();i++) { int v=g[x][i]; if(v!=fa) { ans+=f(v,1,x); } } if(st==1||fa==-1) //不放灯的决策,当这个结点是根节点或他的父节点已经放灯 { int sum=0; for(int i=0;i<g[x].size();i++) { int v=g[x][i]; if(v!=fa) { sum+=f(v,0,x); sum++; } } if(fa!=-1)sum++; ans=min(ans,sum); } return ans; } int main() { int t; scanf("%d",&t); for(int ca=1;ca<=t;ca++) { scanf("%d%d",&n,&m); for(int i=0;i<n;i++)g[i].clear(); for(int i=0;i<m;i++) { int a,b; scanf("%d%d",&a,&b); g[a].pb(b); g[b].pb(a); } memset(dp,-1,sizeof(dp)); int num=0; for(int i=0;i<n;i++)if(dp[i][0]==-1)//可能有多课树 num+=f(i,0,-1); printf("%d %d %d\n",num/2000,m-num%2000,num%2000); } return 0; }