zoukankan      html  css  js  c++  java
  • Test 6.29 T2 染色

    问题描述

    于是 CJK 轻轻松松就切了第一题。“好,那么来看看第二题吧。” JesseLiu 大手一挥,CJK 眼前立刻出现了一棵有 n 个节点的树。“现在,你将要为这颗树染色。你每对一个点染色,将要花费相应的费用。当你对某一点染色后,与它相邻的点也会被染色。你的任务是:在 1s 之内,求出花费最少的染色方案,使得树上所有的点都被染色。”

    输入格式

    第一行 n,表示树中结点的数目。 接下来的 n 行,每一行包含的整数依次为:点的标号 i(1<=i<=n),在点 i 染色费用 k,点 i 的子结点数目 m,接下来 m个数为区域 i 的子结点编号。注意:i 不一定按顺序给出,根节点不一定为 1。

    输出格式

    最小费用。

    样例输入输出

    样例输入1

    6
    1 30 3 2 3 4
    2 16 2 5 6
    3 5 0
    4 4 0
    5 11 0
    6 5 0

    样例输出1

    25

    样例输入2

    5
    1 12 2 2 3
    2 10 2 4 5
    3 5 0
    4 11 0
    5 20 0

    样例输出2

    15

    解析

    显然,这是一道树形DP题。一个节点要被染色,有且仅有以下3种情况:

    • 这个点被某个子节点覆盖。
    • 这个点被父节点覆盖。
    • 这个点自己被选中。

    (f[i])表示以(i)为根的子树被完全覆盖的最小代价,那么以上三种情况分别设为(f[i][0],f[i][1],f[i][2])。我们分别讨论3种情况的状态转移方程。

    设当前节点为(i),父节点为(fa),任意子节点为(j)

    对于第一种,我们必须保证节点的子节点有一个被覆盖且自己没有被覆盖。那么(f[i][0])只能由(f[j][0])(f[j][2])推得。为了保证有子节点被染色且答案最小,我们再分两种情况:若存在(j)使(f[j][2]<f[j][0]),那么就不用管。若不存在,则将(f[j][2]-f[j][0])最小的(j)的贡献强制转为(f[j][2])。状态转移方程如下:

    [f[i][0]=sum min(f[j][0],f[j][2])+min(f[j][2]-f[j][0],0) ]

    对于第二种,(i)的子节点只能被自己的儿子覆盖。注意这里不需要将父亲的代价计入,由于该状态只对第三种情况产生影响,在回到上一层时会计算的。状态转移方程如下:

    [f[i][1]=sum f[j][0] ]

    对于第三种,则是(j)的三种状态取最小值再加上自己的代价。状态转移方程如下:

    [f[i][2]=sum min(f[j][0],f[j][1],f[j][2])+w[i] ]

    注意叶子节点的边界。

    代码

    #include <iostream>
    #include <cstdio>
    #define N 100002
    using namespace std;
    const int inf=1<<30;
    int head[N],ver[N*2],nxt[N*2],l;
    int n,i,j,w[N],f[N][3];
    void insert(int x,int y)
    {
    	l++;
    	ver[l]=y;
    	nxt[l]=head[x];
    	head[x]=l;
    }
    void dfs(int x,int pre)
    {
    	int min1=inf,id=0,sum0=0,sum1=0,sum=0;
    	for(int i=head[x];i;i=nxt[i]){
    		int y=ver[i];
    		if(y!=pre){
    			dfs(y,x);
    			int tmp=f[y][2]-f[y][0];
    			if(tmp<0) min1=0;
    			else if(tmp<min1) min1=tmp,id=y;
    			int t1=min(f[y][0],f[y][2]);
    			sum0+=t1;
    			sum1+=min(t1,f[y][1]);
    			sum+=f[y][0];
    		}
    	}
    	f[x][0]=sum0+min1;
    	if(f[x][0]==inf) f[x][0]=w[x];
    	f[x][1]=sum;
    	f[x][2]=sum1+w[x];
    }
    int main()
    {
    	freopen("color.in","r",stdin);
    	freopen("color.out","w",stdout);
    	cin>>n;
    	for(i=1;i<=n;i++){
    		int x,y,m;
    		cin>>x;
    		cin>>w[x]>>m;
    		for(j=1;j<=m;j++){
    			cin>>y;
    			insert(x,y);
    			insert(y,x);
    		}
    	}
    	dfs(1,-1);
    	int ans=min(f[1][0],f[1][2]);
    	cout<<ans<<endl;
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
    

    总结

    这道题做错,是因为DP的情况没有考虑完整导致错误。下次一定要注意。

  • 相关阅读:
    php无法连接mongodb 3.0问题解决
    mongodb安全配置
    RedHat6/Centos6.5安装mongodb php driver
    RedHat6/Centos6.5安装mongodb
    ASP.NET Identity 2集成到MVC5项目--笔记02
    ASP.NET Identity 2集成到MVC5项目--笔记01
    C#实体类序列化为XML
    MVC4学习笔记之--身份认证过滤器
    【WPF】学习笔记(三)——这个家伙跟电子签名板有个约定
    【WPF】学习笔记(二)——依旧是一个电子签名板
  • 原文地址:https://www.cnblogs.com/LSlzf/p/11111677.html
Copyright © 2011-2022 走看看