很明显的树形DP了,设状态dp[i][0],dp[i][1]。枚举子节点放或不放的两种状态。
在此学到一种不同于一般处理的方法,题目要求被两灯照亮的边尽量多,反过来即被一灯照亮的尽量少设为e。又需要的灯尽量少设为v。
设M是一个很大的数,则M*v+e即是所求。由于M很大,所以主导作用取决于v,只要v不同M*v+e一定不会相同。当v相同,被一灯照亮的尽量少即v此时发挥作用。所以DP时只需要保存这种状态即可。
#include <iostream> #include <cstdio> #include <cstring> #include <vector> using namespace std; int dp[1050][2]; bool vis[1050]; vector<int>t[1050]; const int M=2500; int n,m; void dfs(int u,int f){ vis[u]=true; dp[u][1]=M; dp[u][0]=0; int sz=t[u].size(); for(int i=0;i<sz;i++){ if(t[u][i]!=f){ dfs(t[u][i],u); dp[u][0]+=dp[t[u][i]][1]+1; dp[u][1]+=dp[t[u][i]][0]<dp[t[u][i]][1]?dp[t[u][i]][0]+1:dp[t[u][i]][1]; } } } int main(){ int T,u,v; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); for(int i=0;i<n;i++) t[i].clear(),vis[i]=false; for(int i=1;i<=m;i++){ scanf("%d%d",&u,&v); t[u].push_back(v); t[v].push_back(u); } int ans=0; for(int i=0;i<n;i++){ if(!vis[i]){ dfs(i,-1); ans+=min(dp[i][1],dp[i][0]); } } printf("%d %d %d ",ans/M,m-ans%M,ans%M); } return 0; }