樹形dp(燈與街道)
阿新 • • 發佈:2018-11-12
https://cn.vjudge.net/contest/260665#problem/E
題意:
給你一個n個點m條邊的無向無環圖,在儘量少的節點上放燈,使得所有邊都被照亮。每盞燈將照亮以它為一個端點的所有邊。
在燈的總數最小的前提下,被兩盞燈同時被照亮的邊數應該儘量大。
solution:
這是LRJ《訓練指南》上的例題。
這題教會了我一個很有用的技巧:有兩個所求的值要優化,比如讓a儘量小,b也儘量小
那麼可以轉化為讓 M*a+b儘量小,其中M應該是一個比“a的最大值和b的最小值之差”還要大的數
最終的答案為ans/M, ans%M
回到這題,要求放的燈總數最小,被兩盞燈同時照亮的邊數儘量大。
因為每條邊要麼被一盞燈照亮,要麼被兩盞燈照亮,所以可以轉換為:
求:放的燈總數量最少,被一盞燈照亮的邊數儘量少。
就可以變成球 M*a+b 的最小值,a為放置的燈數量,b為被一盞燈照的邊數
f[u][1]表示u點放燈時的整個子樹最小值
f[u][0]表示u點不放燈時的整個子樹最小值
如果u放,那麼u個子結點可以選擇放,也可以不放,選擇其中較小的值。如果選的是不照,就要增加一條只有一個燈照的邊
如果u不放,那麼其子結點就必須選擇要放,而且每條邊都只有一個燈照
1 /*************************************************************************View Code2 > File Name: a.cpp 3 > Author: QWX 4 > Mail: 5 > Created Time: 2018/10/16 11:38:09 6 ************************************************************************/ 7 8 9 //{{{ #include 10 #include<iostream> 11 #include<cstdio> 12 #include<algorithm> 13 #include<vector> 14#include<cmath> 15 #include<queue> 16 #include<map> 17 #include<set> 18 #include<string> 19 #include<cstring> 20 #include<complex> 21 //#include<bits/stdc++.h> 22 #define vi vector<int> 23 #define pii pair<int,int> 24 #define mp make_pair 25 #define pb push_back 26 #define first fi 27 #define second se 28 #define pw(x) (1ll << (x)) 29 #define sz(x) ((int)(x).size()) 30 #define all(x) (x).begin(),(x).end() 31 #define rep(i,l,r) for(int i=(l);i<(r);i++) 32 #define per(i,r,l) for(int i=(r);i>=(l);i--) 33 #define FOR(i,l,r) for(int i=(l);i<=(r);i++) 34 #define cl(a,b) memset(a,b,sizeof(a)) 35 #define fastio ios::sync_with_stdio(false);cin.tie(0); 36 #define lson l , mid , ls 37 #define rson mid + 1 , r , rs 38 #define INF 0x3f3f3f3f 39 #define LINF 0x3f3f3f3f3f3f3f3f 40 #define ll long long 41 #define ull unsigned long long 42 #define dd(x) cout << #x << " = " << (x) << "," 43 #define de(x) cout << #x << " = " << (x) << "\n" 44 #define endl "\n" 45 using namespace std; 46 //}}} 47 48 49 const int N=1007; 50 const int Z=2000; 51 52 int n,m; 53 int dp[N][2]; 54 vi G[N]; 55 bool vis[N]; 56 57 58 void dfs(int u) 59 { 60 vis[u]=1; 61 dp[u][0]=0; 62 dp[u][1]=Z; 63 for(auto v:G[u])if(!vis[v]){ 64 dfs(v); 65 dp[u][0]+=dp[v][1]+1; 66 dp[u][1]+=min(dp[v][1],dp[v][0]+1); 67 } 68 } 69 70 int main() 71 { 72 fastio; 73 int T;cin>>T; 74 while(T--){ 75 rep(i,0,n)G[i].clear(); 76 cin>>n>>m; 77 rep(i,0,m){ 78 int a,b; cin>>a>>b; 79 G[a].pb(b); 80 G[b].pb(a); 81 } 82 cl(vis,0); 83 int ans=0; 84 rep(i,0,n)if(!vis[i]){ 85 dfs(i); 86 ans+=min(dp[i][0],dp[i][1]); 87 } 88 cout<<ans/Z<<" "<<m-ans%Z<<" "<<ans%Z<<endl; 89 } 90 return 0; 91 }