Jzoj-Link:(虽然我有一个点RE了)
Treeland是一个有n个城市组成的国家,其中一些城市之间有单向边连通。在这个国家中一共有n-1条路。我们知道,如果我们不考虑路的方向,那么我可以从任意城市到达任意城市。
最近,Treeland的总理Candy为了发展经济,想要从这n个城市中选择一个作为Treeland的首都,首都必须要能到达其他任意城市,这使得有些道路必须反向,付出的代价即需要反向的道路条数。
Candy想要选择一个城市作为首都,使得付出的代价最小。可能有多个城市满足条件,按编号从小到大输出。
Input
第一行,一个整数n,表示城市个数
接下来n-1行,每行两个整数x、y,表示城市x到城市y之间有一条单向路径
Output
第一行,一个整数k,花费的最小代价。
第二行若干个整数,中间用空格隔开,表示满足条件的城市编号。行末没有多余的空格。
Sample Input
Sample Input1:
3
2 1
2 3
Sample Input2:
4
1 4
2 4
3 4
Sample Output
Sample Output1:
0
2
Sample Output2:
2
1 2 3
考虑暴力得出1号点的价值,然后再转移答案。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int l[200010], ans[200010], ZZZans[200010], n, aa, bb, t, Ans = 20000000, lj;
bool b[200010];
//ans[d]为选d节点为首都时需要改变的边数
//ZZZans存最终答案(符合条件的点)
struct asdf{
int to, next, z;
} A[500001];
void dfs(int d){ //暴力求1号点的价值 //d为当前点
b[d] = 1; //标记
for(int i = l[d]; i; i = A[i].next)
if(b[A[i].to] == 0){ //如果没走过
dfs(A[i].to); //搜
ans[d] += ans[A[i].to]; //加上答案
if(A[i].z == 0) ++ans[d]; //如果这条边本身就是逆着下去的,+1
}
b[d] = 0;
}
void add(int aaa,int bbb,int ccc){ //建边 ccc=1表示顺着,ccc=0表示逆着
A[++t].to = bbb;
A[t].z = ccc;
A[t].next = l[aaa];
l[aaa] = t;
}
void s(int D, int xShun, int Deep){ //当前点,从一号点顺下来的有多少,深度
int shun = Deep - xShun, ni = xShun; //统计
int sum = ans[1] - shun + ni;
if(sum < Ans){ //更新答案
Ans = sum;
lj = 1;
ZZZans[lj] = D;
}
else if(sum == Ans) ZZZans[++lj] = D; //记录
b[D] = 1;
for(int i = l[D]; i; i = A[i].next)
if(b[A[i].to] == 0)
s(A[i].to, xShun + A[i].z, Deep+1); //搜
}
int main(){
scanf("%d",&n);
for(int i = 1; i < n; ++i){
scanf("%d%d",&aa,&bb);
add(aa, bb, 1); //aa-bb是正边
add(bb, aa, 0); //bb-aa是逆边
}
dfs(1);
s(1, 0, 0);
sort(ZZZans + 1, ZZZans + 1 + lj); //排序
printf("%d\n",Ans); //输出
for(int i = 1; i <= lj; ++i)
printf("%d ",ZZZans[i]);
}