1.只需要求出最大子树中节点数最小的数目即可
题意:有一个国王要把他的领土分给两个儿子,国王的领土是一棵树,N个结点,N-1条边把这些结点连起来,现在大小儿子要选择一个点作为他的首都,那么除首都分别是这两个儿子之外,其他的城市(结点)根据离谁近就归谁所有,如果一样远的话就归大儿子所有,现在假设两个人都采取最优策略,且大儿子先选,问大儿子最多能够得到多少城市?
解法:如果大儿子选择了一个点P,那么这个小儿子选择的点一定在P点的某个子树(S)当中,且P的其他子树一定归大儿子所有,对于子树S,小儿子的最优策略显然是选择和P相邻的哪一个点,否则将损失更多的城市。那么最后的解法就是大儿子选择P,P满足P的最大子树节点数最少。
设ans是最大子树节点数,则该题答案为 N-ans
代码如下:
//**********
板子
//**********
#include <cstdio>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <cstring>
#include <utility>
#include <string>
#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
using namespace std;
#define eps 1e-10
#define inf 0x3f3f3f3f
typedef long long LL;
const int maxn = 5e4 + 5;
vector<int>G[maxn];
int ans, n;
int dfs(int u, int fa) { //递归转化以u为根的子树,u的父亲是fa
int cnt = 0, maxt = 0;
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
if(v != fa) {
//cout<<"dfs("<<v<<","<<u<<")"<<" ** ";
int d = dfs(v, u); //递归转化以v为根的子树
cnt += d;
//cout<<"cnt="<<cnt<<" d="<<d<<" ** ";
maxt = max(maxt, d);
//cout<<"maxt="<<maxt<<" ** ";
}
}
maxt = max(maxt, n - (cnt + 1)); //n-cnt-1 删除cnt+1之后 剩下的节点数目,找最大子树的节点数
//cout<<"newMaxt="<<maxt<<" ** ";
ans = min(ans, maxt); //删除某个节点后,最大子树的数目里面最小的是哪个,所以ans是全局变量
//cout<<"ans="<<ans<<" ** ";
return cnt + 1; //返回以u为根的子树的节点个数,子树的节点个数和(cnt) + 1(u这一个根节点)
}
int main() {
while(scanf("%d", &n) == 1) {
for(int i = 1; i <= n; ++i) G[i].clear();
int u, v;
for(int i = 0; i < n-1; ++i) {
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
ans = inf;
dfs(1, -1);
printf("%d\n", n - ans);
}
return 0;
}
2.需要求出要删除的点(重心)是哪个,若有多个,升序输出序号
poj3107
/* ***********************************************
板子
************************************************ */
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define showtime fprintf(stderr,"time = %.15f\n",clock() / (double)CLOCKS_PER_SEC)
#define lld %I64d
#define REP(i,k,n) for(int i=k;i<n;i++)
#define REPP(i,k,n) for(int i=k;i<=n;i++)
#define scan(d) scanf("%d",&d)
#define scanl(d) scanf("%I64d",&d)
#define scann(n,m) scanf("%d%d",&n,&m)
#define scannl(n,m) scanf("%I64d%I64d",&n,&m)
#define mst(a,k) memset(a,k,sizeof(a))
#define LL long long
#define N 50005
#define mod 1000000007
inline int read(){int s=0;char ch=getchar();for(; ch<'0'||ch>'9'; ch=getchar());for(; ch>='0'&&ch<='9'; ch=getchar())s=s*10+ch-'0';return s;}
指针版
图解:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
const double eps = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int MOD = 1000000007;
#define ll long long
#define CL(a,b) memset(a,b,sizeof(a))
#define MAXN 100010
struct node
{
int v;
node *next;
}tree[MAXN], *head[MAXN];
int pre,n,dp[MAXN];//dp用来装每个结点的子节点数
int num[MAXN],ans,tot;//num用来装最后的答案
void init()//初始化
{
pre = 1; ans = INF;
CL(dp, 0);
CL(head, NULL);
}
void add(int a, int b)//加边,即建树
{
tree[pre].v = a;
tree[pre].next = head[b]; head[b] = &tree[pre]; pre++;
tree[pre].v = b;
tree[pre].next = head[a]; head[a] = &tree[pre]; pre++;
}
void dfs(int son, int father)//记录每个结点的子节点数
{
node *p = head[son];
dp[son] = 1;
while(p != NULL)
{
if(p->v != father)
{
dfs(p->v, son);
dp[son] += dp[p->v];
}
p = p->next;
}
}
void dp_tree(int son, int father)
{
int maxx = 0;
node *p = head[son];
while(p != NULL)
{
if(p->v != father)
{
dp_tree(p->v, son);
maxx = max(dp[p->v], maxx);
}
p = p->next;
}
//更新答案
maxx = max(n-dp[son], maxx);
if(maxx < ans)
{
tot = 1;
ans = maxx;
num[tot] = son;
}
else if(maxx == ans)
{
tot++;
num[tot] = son;
}
}
int main()
{
int u,v;
while(scanf("%d",&n)==1)
{
init();
for(int i=1; i<n; i++)
scanf("%d%d",&u,&v), add(u, v);
dfs(1, 0);
dp_tree(1, 0);
sort(num+1, num+tot+1);
printf ("%d",num[1]);
for(int i=2; i<=tot; i++)
printf (" %d",num[i]);
printf ("\n");
}
return 0;
}
vector简化版 更容易理解
/**********
板子
**********/
#include <cstdio>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <cstring>
#include <utility>
#include <string>
#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
using namespace std;
#define eps 1e-10
#define inf 0x3f3f3f3f
#define ll long long
#define CL(a,b) memset(a,b,sizeof(a))
#define MAXN 100010
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int maxn = 5e4 + 5;
vector<int>G[maxn];
int ans, n;
int dp[maxn],tot,num[maxn];
void init(){
ans=INF;
CL(dp,0);
}
void dfs(int u, int fa) { //递归转化以u为根的子树,u的父亲是fa,统计各节点 子节点数目
dp[u]=1;
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
if(v != fa) {
dfs(v, u); //递归转化以v为根的子树
dp[u]+=dp[v];
}
}
}
void dp_dfs(int son,int fa){
int maxx=0;
for(int i = 0; i < G[son].size(); ++i) {
int v = G[son][i];
if(v != fa) {
dp_dfs(v, son);
maxx=max(maxx,dp[v]);
}
}
//更新答案
maxx = max(n-dp[son], maxx);
if(maxx < ans)
{
tot = 1;
ans = maxx;
num[tot] = son;
}
else if(maxx == ans)
{
tot++;
num[tot] = son;
}
}
int main()
{
int u,v;
while(scanf("%d",&n)==1)
{
init();for(int i=1;i<n;i++) G[i].clear();
for(int i=1; i<n; i++){
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1, 0);
dp_dfs(1, 0);
sort(num+1, num+tot+1);
printf ("%d",num[1]);
for(int i=2; i<=tot; i++)
printf (" %d",num[i]);
printf ("\n");
}
return 0;
}