较难树形动态规划(bzoj 2466 [中山市选2009]树)

嗯….中山市市选很有做的意义啊
(像我这种中山一中的孩纸)

bzoj 2466 [中山市选2009]树

【问题描述】
图论中的树为一个无环的无向图。给定一棵树,每个节点有一盏指示灯和一个按钮。如果节点的按扭被按了,那么该节点的灯会从熄灭变为点亮(当按之前是熄灭的),或者从点亮到熄灭(当按之前是点亮的)。并且该节点的直接邻居也发生同样的变化。
开始的时候,所有的指示灯都是熄灭的。请编程计算最少要按多少次按钮,才能让所有节点的指示灯变为点亮状态。
【输入格式】
输入文件有多组数据。
输入第一行包含一个整数n,表示树的节点数目。每个节点的编号从1到n。
输入接下来的n – 1行,每一行包含两个整数x,y,表示节点x和y之间有一条无向边。
当输入n为0时,表示输入结束。
【输出格式】
对于每组数据,输出最少要按多少次按钮,才能让所有节点的指示灯变为点亮状态。每一组数据独占一行。

输入样例:
3
1 2
1 3
0
输出样例:
1

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
struct node
{
    int x,y,next;
    //x父亲y儿子
}a[110000];
int len=0,n,last[110000];
int f[1100000][5];
void ins(int x,int y)
{
    len++;a[len].x=x;a[len].y=y;
    a[len].next=last[x];last[x]=len;
}
bool bk[110000];
void treedp(int x)
{
    /*f[x][0]:没开(但被邻居影响)亮了
    f[x][1]:没开 没亮
    f[x][2]:开了 亮了
    f[x][3]:开了 没亮(这个情况不存在啊不讨论)
    f[][]中所存储的都是包含之前的代价的总值*/
    f[x][2]=1;
    int c=0,minn=99999999,t=0;
    for (int k=last[x];k;k=a[k].next)//枚举
    {
        int y=a[k].y;
        if (bk[y]==false)
        {
            bk[y]=true;
            treedp(y);
            f[x][2]+=f[y][1];//继承之前的情况
            //接下来讨论亮时的最小费用
            if (f[y][0]<f[y][2])
            {
                t+=f[y][0];
                minn=min(minn,f[y][2]-f[y][0]);//找能把自己点亮且代价最小的情况
            }
            else
            {
                t+=f[y][2];
                minn=min(minn,f[y][0]-f[y][2]);//找能把自己点亮且代价最小的情况
                c++;//统计次数
            }
        }
    }
    if (c%2==1)//如果影响(被影响)了奇数次,则亮
    {
        f[x][0]=t;
        f[x][1]=t+minn;
    }
    else//否则灭
    {
        f[x][0]=t+minn;
        f[x][1]=t;
    }
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        memset(f,0,sizeof(f));
        if (n==0) break;
        len=0;memset(last,0,sizeof(last));
        for (int i=1;i<n;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            ins(x,y);ins(y,x);
        }
        int root=1;//根
        memset(bk,false,sizeof(bk));bk[root]=true;
        treedp(root);
        printf("%d\n",min(f[root][0],f[root][2]));
    } 
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值