日常刷白书,第三次刷dp刷到这题,竟然还是不会写.....
这个题给定n个点m条边的无向无环图,有至多1000个节点,每个节点有两个状态,可以放灯或者不放灯,要求放最少的灯使得所有边被照亮且被两盏灯同时照亮的边数尽量大。
这个题直接写无从下手,但是可以把问题转化一下,转化为求最少的灯数a且只被一盏灯照亮的边数b尽量小,可以把目标转换成求x=Ma+c的最小值,M可取2000,设i节点放灯的值是d[i][1],不放灯是d[i][0],son表示子节点(多个)然后状态方程就是d[i][1]=2000+min(d[son][1],d[son][0]+1),d[i][0]=d[son][1]+1,接下来就一目了然了
#include<bits/stdc++.h> using namespace std; int n,m,M=2000; int d[1001][2],vis[1001]; vector<int>G[1001]; int dfs(int k) { vis[k]=1; d[k][0]=0; d[k][1]=2000; for(int i=0;i<G[k].size();i++) { int t=G[k][i]; if(vis[t]) continue; dfs(t); d[k][0]+=d[t][1]+1; d[k][1]+=min(d[t][1],d[t][0]+1); } } int main() { int T; scanf("%d",&T); while(T--) { int i,a,b; scanf("%d%d",&n,&m); for(i=0;i<n;i++) { G[i].clear(); d[i][0]=d[i][1]=vis[i]=0; } for(i=0;i<m;i++) { scanf("%d%d",&a,&b); G[a].push_back(b); G[b].push_back(a); } int ans=0; for(i=0;i<n;i++) if(!vis[i]) { dfs(i); ans+=min(d[i][0],d[i][1]); } printf("%d %d %d\n",ans/M,m-ans%M,ans%M); } }