题目大意:
一个森林,将一些点染色,使所有边都只至少有一个端点被染色
使被染色的点尽可能少,在这个前提下,使两端都被染色的边尽可能多
输出染色的点数,两端被染色的边数,一端被染色的边数
思路:
树形dp[i][0/1] 后一维表示它的父亲是否被染色
但是要用到一个骚操作,因为要使两端都被染色的边尽可能多
所以结果ans用tmp*a+b表示,其中b为一端被染色的边,a为灯的个数,tmp为一个很大的数
这样的话使总的ans最小即可,可以很方便的求出灯数和两种要求的边数
具体dp见注释
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstdlib> 5 #include<cstring> 6 #include<algorithm> 7 #include<vector> 8 #include<queue> 9 #define inf 2139062143 10 #define ll long long 11 #define MAXN 1100 12 #define tmp 3000 13 using namespace std; 14 inline int read() 15 { 16 int x=0,f=1;char ch=getchar(); 17 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 18 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 19 return x*f; 20 } 21 int n,m,dp[MAXN][2],nxt[MAXN*2],fst[MAXN],to[MAXN*2],T,cnt,ans; 22 void add(int u,int v) {nxt[++cnt]=fst[u],fst[u]=cnt,to[cnt]=v;} 23 int dfs(int x,bool k,int fa) 24 { 25 if(dp[x][k]) return dp[x][k]; 26 int res=tmp,t1=0; 27 for(int i=fst[x];i;i=nxt[i]) 28 if(to[i]!=fa) res+=dfs(to[i],1,x);//被染色 29 if(!k&&fa!=-1) res++;//它不是根而且父亲没有被染过,它与它的fa是一条单端边 30 if(k||fa==-1)//不染x的情况(x为根或x的fa被染) 31 { 32 for(int i=fst[x];i;i=nxt[i]) 33 if(to[i]!=fa) t1+=dfs(to[i],0,x); 34 if(fa>0) t1++;//该节点不是树根,它与它的fa是一条单端边 35 res=min(res,t1); 36 } 37 return dp[x][k]=res; 38 } 39 int main() 40 { 41 T=read(); 42 int a,b; 43 while(T--) 44 { 45 memset(fst,0,sizeof(fst)); 46 memset(nxt,0,sizeof(nxt)); 47 memset(dp,0,sizeof(dp)); 48 n=read(),m=read(),ans=cnt=0; 49 for(int i=1;i<=m;i++) {a=read()+1,b=read()+1;add(a,b);add(b,a);} 50 for(int i=1;i<=n;i++) if(!dp[i][0]) ans+=dfs(i,0,-1); 51 printf("%d %d %d\n",ans/tmp,m-ans%tmp,ans%tmp); 52 } 53 }