题目链接:https://vjudge.net/contest/307650#problem/B
题目大意:
思路:
因为d(u, 1)和d(u, 2)对子节点来说只要一个区别
d(u, 1):所有子节点都不是服务器
d(u, 2):只有一个子节点是服务器
那么d(u, 1):自己不是服务器,所有子节点都不是服务器,那么他的子节点状态是d(v, 2)
d(u, 1)-d(v, 2)表示u节点现在没有考虑v节点的个数,现在我们希望v是服务器,那么就可以推出d(u, 2),如果v是服务器要加上d(v, 0)。
所以:d(u, 2) = d(u, 1) - d(v, 2) + d(v, 0)
思考:wa了一上午
原因1:初始化在开始前。
原因2:有返回值的函数记得return, 不然一直T或者RE。还一直找不到原因。
对考虑父节点的dp,递推的时候不用考虑父节点的状态,例如d(v, 1)v的父节点是服务器,是不是就要加1,其实不用,因为父节点推d(u, 1)的时候会加,只有dp转移没有问题就可以了。因为父节点在递推的时候会考虑子节点。
当算法复杂度需要优化时,看看之前的状态能不能推导出这个复杂的状态。
减少时间复杂度
#include<bits/stdc++.h>
#define LL long long
using namespace std;
vector<int> e[10010];
int dp[10010][3];
int n;
void dfs(int u, int fa)
{
for(int i=0;i<e[u].size();i++)
{
int v=e[u][i];
if(v!=fa)
{
dfs(v, u);
dp[u][0]+=min(dp[v][1], dp[v][0]);
dp[u][1]+=dp[v][2];
dp[u][2]=min(dp[u][1]-dp[v][2]+dp[v][0], dp[u][2]);
}
}
dp[u][0]++;
}
int main()
{
while(cin>>n)
{
// scanf("%d",&n);
for(int i=0;i<=n+1;i++)
{
e[i].clear();
dp[i][0]=dp[i][1]=0;
dp[i][2]=n+1;
}
int x, y;
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
e[x].push_back(y);
e[y].push_back(x);
}
int k;
scanf("%d",&k);
dfs(1, -1);
printf("%d\n", min(dp[1][0], dp[1][2]));
if(k==-1)
{
break;
}
}
return 0;
}