嗯….中山市市选很有做的意义啊
(像我这种中山一中的孩纸)
【问题描述】
图论中的树为一个无环的无向图。给定一棵树,每个节点有一盏指示灯和一个按钮。如果节点的按扭被按了,那么该节点的灯会从熄灭变为点亮(当按之前是熄灭的),或者从点亮到熄灭(当按之前是点亮的)。并且该节点的直接邻居也发生同样的变化。
开始的时候,所有的指示灯都是熄灭的。请编程计算最少要按多少次按钮,才能让所有节点的指示灯变为点亮状态。
【输入格式】
输入文件有多组数据。
输入第一行包含一个整数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;
}