zoukankan      html  css  js  c++  java
  • [UVA10859 放置街灯 Placing Lampposts]

    [UVA10859 放置街灯 Placing Lampposts]

    一.前言

    ​ 啊啊啊啊啊啊我竟然会做树形DP!!!!!(好吧不会其实

    有一说一UVA是真的慢奥,放个题目链接

    二.思路

    ​ 首先,什么是无向无环图呢?我个人倾向认为是“森林”。很显然的,一棵树上将任意两个点额外加一条边连起来就会有一个环出现,而一个图的生成(包含原图所有点)就是这个图中的最大无向无环图(毕竟再加边就有环了),但是这道题不是最大,相当于是从树上取几条边删掉,就形成了森林。

    ​ 明确了是森林之后,需要将整个森林分为几颗树来解决。对于一个点及他的子树,我们设 (f[x][0],g[x][0]) 分别为 x点无灯时的它的子树(包含它)中最小灯数以及被两个灯所覆盖的道路数。(有灯二维为1)

    ​ 之所以只记录被两个灯所覆盖的,而不是记录三个值,是因为后两个的和为边的总数。

    ​ 由于每一条边都要有灯,显然的,(f[x][0]) 只能由 (f[children[x]][1]) 转移过来。(f[x][1]) 则有可能由 (f[children[x]][1/0])转移过来,这个转移涉及到被两个灯照耀的边数+1,而这个又是第二关键字。种种因素结合起来,得出一段转移代码。

    for(int i=head[x];i!=-1;i=ne[i]){
    		int v=to[i];
    		if(v==fa)continue;
    		dfs(v,x);
    		f[x][0]+=f[v][1];g[x][0]+=g[v][1];
    		if(f[v][1]>f[v][0])f[x][1]+=f[v][0],g[x][1]+=g[v][0];
    		else if(f[v][1]==f[v][0]&&g[v][0]>g[v][1]+1)
    			f[x][1]+=f[v][0],g[x][1]+=g[v][0];
    		else f[x][1]+=f[v][1],g[x][1]+=g[v][1]+1;
    	}
    

    (应该还算简明易懂)

    然后设置边界条件为 (f[x][1]=1) 这是初始值,很显然。

    最后整体采用自叶到根的转移,(DFS),因为从儿子转移过来,并且儿子的决策对父亲没有影响。因为森林,要对每一个树分别dfs。

    三.CODE

    const int MAXN=1005;
    int t,n,m,ans1,ans2;
    int head[MAXN],to[2*MAXN],ne[2*MAXN],tot;
    int f[MAXN][2],g[MAXN][2];
    bool vis[MAXN];
    void add(int x,int y){
    	to[++tot]=y;
    	ne[tot]=head[x];
    	head[x]=tot;
    }
    void dfs(int x,int fa){
    	vis[x]=1;
    	f[x][1]=1;
    	for(int i=head[x];i!=-1;i=ne[i]){
    		int v=to[i];
    		if(v==fa)continue;
    		dfs(v,x);
    		f[x][0]+=f[v][1];g[x][0]+=g[v][1];
    		if(f[v][1]>f[v][0])f[x][1]+=f[v][0],g[x][1]+=g[v][0];
    		else if(f[v][1]==f[v][0]&&g[v][0]>g[v][1]+1)
    			f[x][1]+=f[v][0],g[x][1]+=g[v][0];
    		else f[x][1]+=f[v][1],g[x][1]+=g[v][1]+1;
    	}
    }
    int main(){
    	t=read();
    	while(t--){
    		n=read();m=read();
    		ans1=ans2=tot=0;
    		memset(head,-1,sizeof(head));
    		memset(vis,0,sizeof(vis));
    		memset(g,0,sizeof(g));
    		memset(f,0,sizeof(f));
    		for(int i=1,u,v;i<=m;++i){
    			u=read();v=read();
    			add(u,v);add(v,u);
    		}
    		for(int i=0;i<n;++i){
    			if(!vis[i]){
    				vis[i]=1;
    				dfs(i,-1);
    				if(f[i][0]<f[i][1])ans1+=f[i][0],ans2+=g[i][0];
    				else if(f[i][0]==f[i][1]&&g[i][0]>g[i][1])
    					ans1+=f[i][0],ans2+=g[i][0];
    				else ans1+=f[i][1],ans2+=g[i][1];
    			}
    		}
    		cout<<ans1<<" "<<ans2<<" "<<m-ans2<<endl;
    	}
    	return 0;
    }
    
  • 相关阅读:
    编译pypcap
    python输出重复字符串的简单办法
    Python天天美味(1) 交换变量(转)
    Python天天美味(4) isinstance判断对象类型(转)
    Python天天美味(2) 字符遍历的艺术(转)
    Python天天美味(3) 字符转换(转)
    Python天天美味(5) ljust rjust center(转)
    Python天天美味(6) strip lstrip rstrip(转)
    Python天天美味(10) 除法小技巧(转)
    Python标准库12 数学与随机数 (math包,random包)(转)
  • 原文地址:https://www.cnblogs.com/clockwhite/p/13386196.html
Copyright © 2011-2022 走看看