DP练习1题解D
先上题目描述
样例输入
10
1 2
2 3
3 4
4 5
6 7
7 8
8 9
9 10
3 8
样例输出
3
8
emmm 因为POJ炸了 和讲台上面首A讲的好像有一点小区别(也可能我没听懂)
我不知道能不能过 所以。。
明天看下 先写在这里 不贴代码了
//第二天我来了 POJ还是没登上去T_T 那我就贴吧
首先是个图 n(n<=10000)个结点 n-1的边
合适用邻接表存储(不过此题可以用特殊的方式存储)见代码
也就是个树
是个树状DP
断掉一个点后 它之下的各个子树 和除了该点和它之下的点这棵树 构成了森林
要让每个树节点数小于等于n/2
断掉一个点要满足条件 也就是要除了该点和它之下的点总数小于等于n/2
并且它的子树中最大的树节点数小于等于n/2
我们可以从底部出发动归 传递上述两个状态 如果有点符合就输出
我的思路是用s数组存储相连的边数
d表存储连接情况 a数组存储它之下的点总数 和之下的最大的树节点数
拓扑排序结合DP
如果只有一条边和他相连 加入队列q
q不空就进行循环
取出一个只剩一个边的元素
判断是否符合 符合输出
相连的是它的父节点 向他传递状态
然后删除节点 更新父节点的表
以上
ps:GH当时刷不出来 没看 没做 明天写 要看英语了T_T要考四级了了了了了了了了了了
再:第二天起来真香了
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
int s[10001],d[10001];
int a[10001][3];
int main()
{
int i,u,v,n,son,father;
queue<int> q;
while(cin>>n)
{
memset(a,0,sizeof(a));
memset(s,0,sizeof(s));
memset(d,0,sizeof(d));
for (i=1;i<=n-1;i++)
{
cin>>u>>v;
s[u]++;
s[v]++;
d[u]=d[u]+v;
d[v]=d[v]+u;
}
for (i=1;i<=n;i++)
if (s[i]==1) q.push(i);
while (!q.empty())
{
son=q.front();
q.pop();
father=d[son];
if (a[son][1]+1>=n/2&&a[son][2]<=n/2) cout<<son<<endl;
if (father!=0)
{
a[father][1]=a[father][1]+a[son][1]+1;
if (a[son][1]+1>a[father][2]) a[father][2]=a[son][1]+1;
d[father]=d[father]-son;
s[father]--;
if (s[father]==1) q.push(father);
}
}
}
return 0;
}
也不知道对不对。。。等好了评测下吧