首页 > 试题广场 >

军营选择

[编程题]军营选择
  • 热度指数:369 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 64M,其他语言128M
  • 算法知识视频讲解
牛牛是一名新晋营长,需要选择军营建造地点,已知一共有 个候选地,编号为 ,有 条道路,使得这 个候选地之间两两可以到达。

军营选择规则如下:

对于其中一个候选地 而言,如果将该地以及其直接相连的道路全都删除,就可以得到若干个连通块,用 记录下其中的最大连通块中的候选地数量。

对于所有的满足 的候选地 而言,都是最佳军营建造地。

由于最佳地点可能不止一个,所以牛牛想要通过一些操作将该地点唯一化:

首先,牛牛会封闭一条已经存在的道路,接着,构建一条新道路,在这两个操作之后,这 条道路依然可以使 个地点两两相通,同时,最佳地点只有一个。

但是,牛牛只擅长指挥军队,并不精通此法,所以,请你给出任意一种可以达成要求的合法方案。

输入描述:
本题为多组测试数据,第一行输入一个正整数 ,代表测试数据组数。

对于每组测试数据,第一行输入一个正整数 ,代表军营候选地的数量。
接下去 行,每行两个正整数 ,代表候选地 之间存在一条道路 (无向边)。

数据保证,每组测试数据给出的道路一定可以使 个候选地两两相通,同时,所有测试数据的 之和不会超过 .


输出描述:
对于每组测试数据,输出两行,第一行输出两个正整数 ,代表封闭原道路 ,这条道路必须存在。
第二行输出两个正整数 ,代表增加一条道路 ,这条道路必须在原道路中不存在或者已经被封闭。
由于是无向边,所以输出 等价于 .

如果存在多种解,任意输出一种即可,只需要保证满足题意。
示例1

输入

2
3
1 2
1 3
4
1 2
1 3
4 3

输出

1 2
2 1
3 4
4 1

说明

第一组测试数据中,最佳选择本就只有一个,所以任选一条道路,封闭后再建造即可。
第二组测试数据中,最佳选择为候选地 \text 1,\ \text 3,而在封闭 \text {3 4} 之间的道路,再添加 \text {4 1} 之间的道路之后,最佳选择就只有候选地 \text 1.
连通树。以1为树根DFS,计算每个结点为树根时的结点总数,同时找到每个节点子树结点数的最大值。当我们删除某个结点i时,wi,即剩余连通块最大值出现在max(i所有子树结点数最大值,n-i为树根时的结点总数)。按题意找到最小值。
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int t,n,w[100005],minnode=1e9;
vector<int>e[100005];
int dfs(int root,int f)
{ /**< dfs返回值是以root为根子树的节点数量*/
    int sum=1,maxv=0;
    for(int i=0;i<e[root].size();i++)
    {
        int y=e[root][i];
        if(y==f) continue;
        int temp=dfs(y,root);
        sum+=temp;
        maxv=max(maxv,temp);/**< 求root子树节点数最大值 */
    }
    w[root]=max(n-sum,maxv);/**< 记录下删除root后的最大连通块wi */
    minnode=min(minnode,w[root]);/**< 求最小wi的值 */
    return sum;
}
int main()
{
    ios::sync_with_stdio(0),cin.tie(0);
//    freopen(".in","r",stdin);
//    freopen(".out","w",stdout);
    int i,j,x,y;
    cin>>t;
    while(t--)
    {
        minnode=1e9;
        cin>>n;
        for(i=1;i<=n;i++)
            e[i].clear(),w[i]=0;
        for(i=1;i<n;i++)
        {
            cin>>x>>y;
            e[x].push_back(y),e[y].push_back(x);
        }
        dfs(1,0);
        int s1=0,s2=0;
        for(i=1;i<=n;i++)
            if(w[i]==minnode)
            {/**< 找到2个最小值的节点,感觉树应该是对称的 */
                if(s1==0)
                    s1=i;
                else
                    s2=i;
            }
        if(s2==0)/**< 只有一个节点 */
            cout<<1<<' '<<e[1][0]<<endl<<1<<' '<<e[1][0]<<endl;
        else
        {   /**< 把第二个节点中一颗子树转移给第一个节点 */
            for(i=0;i<e[s2].size();i++)
                if(e[s2][i]!=s1)
                break;
            cout<<s2<<' '<<e[s2][i]<<endl<<s1<<' '<<e[s2][i]<<endl;
        }
    }
    return 0;
}


发表于 2021-05-10 21:09:22 回复(1)

热门推荐

通过挑战的用户