传送门:caioj
题目描述
【问题描述】 图论中的树为一个无环的无向图。给定一棵树,每个节点有一盏指示灯和一个按钮。如果节点的按扭被按了,那么该节点的灯会从熄灭变为点亮(当按之前是熄灭的),或者从点亮到熄灭(当按之前是点亮的)。并且该节点的直接邻居也发生同样的变化。 开始的时候,所有的指示灯都是熄灭的。请编程计算最少要按多少次按钮,才能让所有节点的指示灯变为点亮状态。
【输入格式】
输入文件有多组数据。
输入第一行包含一个整数n,表示树的节点数目。每个节点的编号从1到n。
输入接下来的n – 1行,每一行包含两个整数x,y,表示节点x和y之间有一条无向边。
当输入n为0时,表示输入结束。
【输出格式】
对于每组数据,输出最少要按多少次按钮,才能让所有节点的指示灯变为点亮状态。每一组数据独占一行。
输入样例:
3
1 2
1 3
0
输出样例:
1
【数据规模】
对于20%的数据,满足1 <= n <=15。
对于40%的数据,满足1 <= n <=50。
对于100%的数据,满足1 <= n <=100。
分析
申明:本题所用方法为树形dp,如要研究高(%)斯(%)消(%)元,请转大佬题解(随手搜的)
1.令f[i][0/1/2]表示状态:
开关 灯
f[i][0]:i不开 亮
f[i][1]:i不开 灭
f[i][2]:i开 亮
PS:–保证i亮时,整棵子树都亮;i不亮时,子树的其余孩子都亮
—当i开着但不亮时,那就废了(tan90º)
2.初始化:f[i][2]=1(本身要开一次),其余为0
3.转移方程:
令s=sum(min(f[v][0],f[v][2] )) //取最小
mi=min( abs(f[v][0]-f[v][2]) ) //强行转化时需要
if(f[i][2]< f[i][0] ) t^=1 //记录孩子开的次数 t==1表示奇数次 t==0表示偶数次
f[u][0]=s+ (t?0:mi) 如果t==1则u是亮的,否则强行转化成奇数次
f[u][1]=s+ (t?mi:0) 如果t==0则u是灭的,否则强行转化成偶数次
f[u][2]=sum(f[v][1])+1
代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define open(s) freopen(s".in","r",stdin); freopen(s".out","w",stdout);
#define close fclose(stdin); fclose(stdout);
using namespace std;
struct Edge
{
int to;
int nxt;
inline void clr()
{
to=nxt=0;
}
};
int cnt;
int head[105];
Edge edge[205];
inline void add(int x,int y)
{
edge[++cnt]=(Edge){y,head[x]};
head[x]=cnt;
}
int n;
int f[105][3];
inline int read()
{
int k=1;
int sum=0;
char c=getchar();
for(;'0'>c || c>'9' ;c=getchar())
if(c=='-') k=-1;
for(;'0'<=c && c<='9';c=getchar())
sum=sum*10+c-'0';
return sum*k;
}
inline void write(int x)
{
if(x<0) { putchar('-'); x*=-1; }
if(x>9) write(x/10);
putchar(x%10+'0');
}
inline int min(int x,int y)
{
return x<y?x:y;
}
inline void dfs(int u,int pre)
{
f[u][2]=1;
int sum=0,mi=99999999,t=0;//注意mi的初始值,如果过大(eg.0x3f3f3f3f),当u为叶子节点时,其相当于给f[u][0/1]赋初值
for(int i=head[u];i;i=edge[i].nxt)
if(edge[i].to!=pre)
{
int v=edge[i].to;
dfs(v,u);
f[u][2]+=f[v][1];
if(f[v][0]<=f[v][2])
{
sum+=f[v][0];
mi=min(mi,f[v][2]-f[v][0]);
}else
{
sum+=f[v][2];
mi=min(mi,f[v][0]-f[v][2]);
t^=1;
}
}
if(t)
{
f[u][0]=sum;
f[u][1]=sum+mi;
}else
{
f[u][0]=sum+mi;
f[u][1]=sum;
}
}
inline void clean()
{
memset(head,0,sizeof(head));
memset(f,0,sizeof(f));
for(;cnt;) edge[cnt--].clr();
}
int main()
{
open("1113");
for(;scanf("%d",&n)!=EOF;)
{
if(!n) break;
clean();
for(int i=1,x,y;i<n;++i)
{
x=read(); y=read();
add(x,y); add(y,x);
}
dfs(1,-1);
write(min(f[1][0],f[1][2]));
putchar('\n');
}
close;
return 0;
}