题目链接
题目大意:
这是一个交互题,一棵固定的树,题目给出所树上的边,然后我们可以询问不超过
次,每次询问是输入两个节点,然后会返回这两个询问节点的最近公共祖先,最后要求输出树的根节点。
解题思路:
我们可以每次对两个叶子节点进行询问,得到的答案是他们的lca。因为他们有lca所以以lca为根节点的这两个叶子节点所在的子树上的所有的点都不可能是树的根节点,所以可以将这两颗子树上的点都删掉。这个删除操作我们使用dfs就可以了。
为什么?因为每次询问的两个节点,如果lca不是他们其中的一点,所以每次都至少删掉这两个点。如果lca是其中的一个点,因为我们询问的都是叶子节点,所以此时lca就是我们需要的答案。所以不管是哪种情况操作次数都不会超过
。
解题代码:
#include<bits/stdc++.h>
using namespace std;
vector<int> t[1005];
bool visit[1005];
int d[1005];
int lca;
// dfs做子树的清除工作。
void dfs(int u,int f)
{
//碰到lcp就直接返回 因为只是处理这棵子树,根节点不能处理
if (u==lca)
return ;
visit[u]=true;
for (int j=0;j<t[u].size();j++)
d[t[u][j]]--;
for (int i=0;i<t[u].size();i++)
{
int to=t[u][i];
if (to==f)
continue;
if (!visit[to])
dfs(to,u);
}
}
int main()
{
int n,t1,t2;
cin>>n;
for (int i=1;i<n;i++)
{
cin>>t1>>t2;
d[t1]++;
d[t2]++;
t[t1].push_back(t2);
t[t2].push_back(t1);
}
int c=0,mi=n/2;
int ans1,ans2;
while (true)
{
ans1=ans2=-1;
for (int i=1;i<=n && ans2==-1;i++)
{
if (!visit[i] && d[i]==1)
{
if (ans1==-1)
ans1=i;
else
ans2=i;
}
}
if (ans2==-1)
break;
cout<<"? "<<ans1<<" "<<ans2<<endl;
cout.flush();
cin>>lca;
dfs(ans1,ans1);
dfs(ans2,ans2);
}
for (int i=1;i<=n;i++)
{
if(!visit[i])
{
cout<<"! "<<i<<endl;
break;
}
}
cout.flush();
}