【树形DP】树

树(tree)



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

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

这道题目莫名让我想到了以前见过有一个死胖子用手指按字母然后附近的都会点到,不过我忘记题目了~~
言归正传,题目已经很明确告诉你是一棵树,那么就可以很容易想到是树形DP,寻找状态转移方程是本题的关键。
首先,我们用f[x][0]表示以x为根节点的子树下,x开着灯,而他的儿子,孙子,孙子的儿子...也都开灯的最少次数;

f[x][1]表示以x为根节点的子树下,x关灯,而他的子辈都开着灯的最少次数;

f[x][2]则表示以x为根节点的子树下,x关灯,他的子辈也都关灯的最少次数;

那么f[x][3]表示什么呢,就是以x为根节点的子树下,x开灯,他的子辈也都关灯的最少次数,很显然,这是不存在的,所以f[x][3]排除啦。

把F数组的定义弄清楚了,那么接下来的就好办了!
也是分三步:

  1. f[x][0]最好算,因为如果我和我的子辈都开着灯了,那么我的亲朋好友就不用开灯了啦,所以f[x][0]+=f[x][2]

2.接着就算f[x][2]f[x][1]怎么算呢?因为我们还要考虑到有可能在我的子辈按按钮按了个奇数,那么最后的状态就要改变呀(ps:真是个无聊的儿子。。),所以我需要一个变量来算我途中按了多少次(这个变量我称为按钮计数)。然后就是如何“科学的推卸责任了”,我们也要用一个变量,来表示最少次数,那就是tmp+=min(f[x][2],f[x][1])啦!

3.f[x][2]和f[x][1]怎么继承呢?那就是青——藏——高——原,啊,说偏了,这里就体现了我们那个最最最最最可爱的按钮计数了(2点说过,不知道给我重看!),这里我可以告诉你一个技巧,奇数和偶数是刚好相反的,因为状态是刚好相反的嘛,所以你想好奇数以后就直接把奇数调换顺序就行了,奇数的话那f[x][2]=t,而偶数的话就是f[x][1]=t,剩下的呢,我先不告诉你,你自己先想,如果想不出就直接看代码啦!

那么我们就找到了状态转移方程,接着就是制作编目录啦,balabala的,整道题就完成啦!
下面我附上代码,代码中有详细解释:

#include<cmath>
#include<cstdio>
#include<cstring>
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;//据说这个比long long更大,不过后面输出要用llu。
//你细心可以发现我跟本没用long long^_^
struct node//边目录
{
    int x,y,next;//x和y表示一条边的两个端点,next表示下一条边的编号
}a[210];int len,last[210];//last表示与当前边相连的最后一条边的编号,len是边的数目
void ins(int x,int y)
{
    len++;//边的数目增加一条
    a[len].x=x;a[len].y=y;//给边赋值
    a[len].next=last[x];last[x]=len;//边的联系
}
int f[210][3];//我们上面讲到的定义
bool b[210];//因为我用的是双向边,而有可能会进入死循环
void treeDP(int x)
{
    /*
    f[x][0]x开灯,开灯
    f[x][1]x不开灯,开灯
    f[x][2]x不开灯,不开灯
    */
    f[x][0]=1;f[x][1]=f[x][2]=0;//一开始的以x为结点的子树(包括点x)全部开灯就有一种方法:点亮x
    //其他的归零
    int minn=99999999,num=0,tmp=0;//这里的minn用的十分巧妙,num就是按钮计数,tmp就是存储最少次数
    for(int k=last[x];k;k=a[k].next)//访问x的亲朋好友
    {
        int y=a[k].y;//找到x点所在的边的另一个点y
        if(b[y]==false)//判断是否找过y
        {
            b[y]=true;treeDP(y);//标记找过,然后递归找y
            f[x][0]+=f[y][2];//如果我和我的子辈都开着灯了,那么我的亲朋好友就不用开灯了啦! 

            tmp+=min(f[y][0],f[y][1]);//去最小的值
            minn=min(abs(f[y][0]-f[y][1]),minn);//这里非常巧妙:
            /*假设f[y][0]>f[y][1]的话,那么此时tmp加的就是f[y][1],而minn里面的就是f[y][0]-f[y][1]
            那么tmp+minn实际上就是f[y][1]+f[y][0]-f[y][1]=f[y][0]
            而反之,tmp加的就是f[y][0],那么minn里面的就是f[y][1]-f[y][0]
            那么tmp+minn就是f[y][0]+f[y][1]-f[y][0]=f[y][1]了
            */
            if(f[y][1]>=f[y][0])num++;
            //如果不开灯比开灯要贵,那么我们肯定选开灯的,而此时就按了一次按钮,所以num++
        }
    }
    if(num%2==1)
    {
        f[x][2]=tmp+minn;//此时,x不开灯,而他的子辈也不开灯,就要让其中一个儿子帮他开灯
        f[x][1]=tmp;//此时,x不开灯,要他的子辈开灯,那怎么无缘无故开灯呢?所以就让他的儿子去开灯
    }
    else
    {
        f[x][2]=tmp;//同上,我说过是相反的
        f[x][1]=tmp+minn;
    }
}
int main()
{
    int n;
    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);
        }
        memset(b,false,sizeof(b));b[1]=true;
        treeDP(1);//从1开始递归,因为1是根节点
        printf("%d\n",min(f[1][0],f[1][1]));//最后找这两个的最小值就行了!
    }
    return 0;
}

谢谢你的观看,举一反三,你可以上caioj.cn去做树形动态规划的8道题目哦!

原文地址:https://www.cnblogs.com/candy067/p/11401969.html

时间: 2024-10-15 12:05:55

【树形DP】树的相关文章

树形DP+树状数组 HDU 5877 Weak Pair

1 //树形DP+树状数组 HDU 5877 Weak Pair 2 // 思路:用树状数组每次加k/a[i],每个节点ans+=Sum(a[i]) 表示每次加大于等于a[i]的值 3 // 这道题要离散化 4 5 #include <bits/stdc++.h> 6 using namespace std; 7 #define LL long long 8 typedef pair<int,int> pii; 9 const double inf = 12345678901234

hdu-4118 Holiday&#39;s Accommodation(树形dp+树的重心)

题目链接: Holiday's Accommodation Time Limit: 8000/4000 MS (Java/Others)     Memory Limit: 200000/200000 K (Java/Others) Problem Description Nowadays, people have many ways to save money on accommodation when they are on vacation.One of these ways is exc

POJ 1849 Two (树形dp 树的直径 两种方法)

Two Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 1232   Accepted: 619 Description The city consists of intersections and streets that connect them. Heavy snow covered the city so the mayor Milan gave to the winter-service a list of st

树形DP 树的最小支配集,最小点覆盖与最大独立集

最小支配集: 从V中选取尽量少的点组成一个集合,让V中剩余的点都与取出来的点有边相连. (点) 最小点覆盖: 从V中选取尽量少的点组成一个集合V1,让所有边(u,v)中要么u属于V1,要么v属于V1 (边) 最大独立集: 从V中选取尽量多的点组成一个集合,让这些点中间没有边项链,也就是说对于任何一条边,u,v不能同时属于集合V1. 1.贪心算法 首先选取一个点为根节点,求出所有节点对应的DFS序列,按照所得序列反向进行贪心,这样保证对于每个点来说,当子树都被处理过之后才会处理该节点 int p[

hdu-2169 Computer(树形dp+树的直径)

题目链接: Computer Time Limit: 1000/1000 MS (Java/Others)     Memory Limit: 32768/32768 K (Java/Others) Problem Description A school bought the first computer some time ago(so this computer's id is 1). During the recent years the school bought N-1 new co

POJ3107Godfather[树形DP 树的重心]

Godfather Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 6121   Accepted: 2164 Description Last years Chicago was full of gangster fights and strange murders. The chief of the police got really tired of all these crimes, and decided to

POJ 3342 Party at Hali-Bula (树形dp 树的最大独立集 判多解 好题)

Party at Hali-Bula Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 5660   Accepted: 2022 Description Dear Contestant, I'm going to have a party at my villa at Hali-Bula to celebrate my retirement from BCM. I wish I could invite all my co

POJ 1655 Balancing Act (树形dp 树的重心)

Balancing Act Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 10596   Accepted: 4398 Description Consider a tree T with N (1 <= N <= 20,000) nodes numbered 1...N. Deleting any node from the tree yields a forest: a collection of one or m

codeforces 455C C. Civilization(树形dp+树的直径+并查集)

题目链接: codeforces 455C 题目大意: 给出一些点,他们之间初始存在一些边,给出两种操作,第一种是查询某个点所在的树的直径,另一种是将两个树合并,要求使合并后的树的直径最小. 题目分析: 首先算取没做操作前的连通块里的树的直径,也就是先dfs一遍,找到深度最大的点,然后从这个点再搜,找到的最远的距离就是这棵树的直径,因为可以证明从根搜深度最大的点一定是树的直径的一个端点,因为它可以通过到达次大的深度的点或者找到与它公共祖先不在根处的获得树的直径. 然后每次合并,我们可以知道得到的

POJ3398Perfect Service[树形DP 树的最大独立集变形]

Perfect Service Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 1518   Accepted: 733 Description A network is composed of N computers connected by N − 1 communication links such that any two computers can be communicated via a unique rou