CodeForces 29D Ant on the Tree
洛谷题目页面传送门 & CodeForces题目页面传送门
题意见洛谷里的翻译。
这题有\(3\)种解法,但只有\(1\)种是正解(这不是废话嘛)。
方法\(1\):最近公共祖先LCA(正解)
真的把它当作一棵树来做。使用父亲表示法,记录每个节点的父亲。可是输入中只能告诉你谁和谁连,并没有说谁是谁的父亲,这该怎么办呢?其实很简单,只需要通过根是\(1\)这个信息,先把\(1\)的父亲设成自己,把所有\(1\)的邻居\(i\)的父亲都设成\(1\),然后再对\(i\)进行如下操作:把所有\(i\)的“还没有父亲”的邻居\(j\)的父亲都设成\(i\),然后对\(j\)进行如下操作:把所有\(j\)的“还没有父亲”的邻居\(k\)的父亲都设成\(j\),然后对\(k\)进行如下操作……这样就做出来一棵树。我们还需要一个二维bool数组,在\(\operatorname{O}\left(n^2\right)\)时间内预处理出对于任意一对节点与叶子节点(也就是说第一维是任意节点,第二维是叶子节点)\((x,y)\),\(x\)是不是\(y\)的祖先。设叶子节点集合为\(l\),然后从\(1\)走到\(l_1\)、从\(l_1\)走到\(l_2\)、……、从\(l_{n-1}\)走到\(l_n\)、从\(l_n\)走到\(1\)。对于每一次走,从起点一直向上走,一直走到是终点的祖先为止(相当于走到原起点和终点的最近公共祖先(LCA)),再向下走到终点,把经过的点都压入答案序列。最后如果答案序列的大小不是\(2n-1\)就输出\(-1\),否则输出答案就可以了。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
vector<int> nei[301]/*邻接表*/,ans/*答案序列*/;
int f[301];//父亲
bool ance[301][301];//[1]是否是[2]的祖先
void mktre(int x){//对x进行如下操作:
for(int i=0;i<nei[x].size();i++)//将所有x的
if(!f[nei[x][i]])/*“孤儿”邻居*/f[nei[x][i]]=x/*收养*/,mktre(nei[x][i])/*并对它进行如下操作……*/;
}
void go(int st,int ed){//从st走到ed
while(!ance[st][ed])st=f[st],ans.push_back(st);//往上走到是ed的祖先为止
vector<int> rev;
while(st!=ed)rev.push_back(ed),ed=f[ed];//本应向下走,但用的是父亲表示法,只能向上走
for(int i=rev.size()-1;i>=0;i--)ans.push_back(rev[i]);//倒过来压入答案序列
}
int main(){
int n/*节点数*/,i;scanf("%d",&n);
for(i=1;i<n;i++){
int x,y;scanf("%d%d",&x,&y);
nei[x].push_back(y);nei[y].push_back(x);
}
f[1]=1;/*1的父亲是自己*/mktre(1);//先对1进行操作
int x=1,y;ance[1][1]=true;
ans.push_back(1);//因为1没有机会压入答案序列,只好特殊招待
while(~scanf("%d",&y)){
int z=y;while(z!=1)ance[z][y]=true,z=f[z];ance[1][y]=true;//预处理ance
go(x,y);x=y;//从l[i]走到l[i+1]
}
go(x,1);
if(ans.size()>(n<<1)-1)return !printf("-1");//大小不是2n-1
for(i=0;i<ans.size();i++)printf("%d ",ans[i]);//输出答案
return 0;
}
这种方法的时间复杂度是\(\mathrm O\!\left(n^2\right)\)。因为做树的时间是边数,\(\mathrm{O}(n)\);走一次的时间是\(\mathrm{O}(n)\),走\(n\)次\(\mathrm O\!\left(n^2\right)\)。对于\(300\)的水数据,简直再容易不过了!
方法\(2\):暴搜
不把这张图当作树来看,而当作图。走的时候,从起点毫无方向感地搜遍全图直到搜到终点为止。走的函数里要再加一个参数,表示走过来的节点,避免再回去,造成死循环。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
vector<int> nei[301]/*邻接表*/,ans/*答案序列*/;
bool dfs(int st/*起点*/,int ed/*终点*/,int prv/*走过来的,避免死循环*/){//暴搜
if(st==ed)return true;//到达了,带回这个喜讯
for(int i=0;i<nei[st].size();i++)//枚举邻居
if(nei[st][i]!=prv&&dfs(nei[st][i],ed,st)){//如果不是走过来的,那看看能不能搜到终点
ans.push_back(nei[st][i]);//此时已经搜到了,压入答案序列
return true;//搜到了,返回
}
return false;//没搜到
}
int main(){
int i,n/*节点数*/;scanf("%d",&n);
for(i=1;i<n;i++){
int x,y;scanf("%d%d",&x,&y);
nei[x].push_back(y);nei[y].push_back(x);
}
int x=1,y;
while(~scanf("%d",&y))dfs(y,x,0),x=y;//从l[i]开始暴搜l[i+1],因为dfs中是回溯时压入答案序列的,是反的,所以起点和终点也要反过来,反反得正
dfs(1,x,0);
ans.push_back(1);//因为1没有机会压入答案序列,只好特殊招待
if(ans.size()>(n<<1)-1)return !printf("-1");
for(i=0;i<ans.size();i++)printf("%d ",ans[i]);
return 0;
}
暴搜中枚举邻居,每个邻居都可能将整个图遍历一遍,\(\mathrm O\!\left(n^2\right)\),最多有\(n\)次暴搜,所以整个时间复杂度是\(
\mathrm O\!\left(n^3\right)\)。这时间复杂度是在欺负出题人的数据范围吗?
方法\(3\):Floyd指路
聪明的读者也许一定没想到,这题还可以用Floyd吧!先用Floyd算出任意两点的最短路(邻居距离为\(1\)),然后在走的时候,就有方向、不盲目、不彷徨、自信了,很显然,走能够缩短与终点的距离的邻居呗!在这里,Floyd起到了指路的作用。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f//设成INT_MAX相加时会爆int
int dis[301][301]/*最短距离*/,n/*节点数*/;
vector<int> ans;//答案序列
void go(int st,int ed){//从st走到ed
if(st==ed)return;//到达,返回
for(int i=1;/*一定有能走的点,所以不需要终止条件*/;i++)//枚举每个点
if(dis[st][i]==1/*是邻居*/&&dis[i][ed]<dis[st][ed]/*能缩短距离*/){//走
ans.push_back(i);//压入答案序列
go(i,ed);//走一步
return;//能走的点只有一个,找到了就算成功了,不再找了
}
}
int main(){
int i,j;scanf("%d",&n);
for(i=1;i<=n;i++)for(j=1;j<=n;j++)dis[i][j]=i==j?0:inf;//初始化dis
for(i=1;i<n;i++){
int x,y;scanf("%d%d",&x,&y);
dis[x][y]=dis[y][x]=1;//邻居的距离为1
}
//Floyd
for(int k=1;k<=n;k++)for(i=1;i<=n;i++)for(j=1;j<=n;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
// for(i=1;i<=n;i++){for(j=1;j<=n;j++)printf("dis[%d][%d]=%d\t",i,j,dis[i][j]);puts("");}
int x=1,y;
ans.push_back(1);//因为1没有机会压入答案序列,只好特殊招待
while(~scanf("%d",&y))go(x,y),x=y;//从l[i]走到l[i+1]
go(x,1);
if(ans.size()>(n<<1)-1)return !printf("-1");
for(i=0;i<ans.size();i++)printf("%d ",ans[i]);
return 0;
}
虽然有方向了,但也要为此付出代价——奇慢无比的Floyd。所以时间复杂度还是\(\mathrm O\!\left(n^3\right)\)。题目被虐了,出题人好可怜
CodeForces 29D Ant on the Tree的更多相关文章
- codeforces 29D Ant on the Tree (dfs,tree,最近公共祖先)
D. Ant on the Tree time limit per test 2 seconds memory limit per test 256 megabytes input standard ...
- Codeforces 29D Ant on the Tree 树的遍历 dfs序
题目链接:点击打开链接 题意: 给定n个节点的树 1为根 则此时叶子节点已经确定 最后一行给出叶子节点的顺序 目标: 遍历树并输出路径.要求遍历叶子节点时依照给定叶子节点的先后顺序訪问. 思路: 给每 ...
- codeforces 704B - Ant Man 贪心
codeforces 704B - Ant Man 贪心 题意:n个点,每个点有5个值,每次从一个点跳到另一个点,向左跳:abs(b.x-a.x)+a.ll+b.rr 向右跳:abs(b.x-a.x) ...
- codeforces 741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(启发式合并)
codeforces 741D Arpa's letter-marked tree and Mehrdad's Dokhtar-kosh paths 题意 给出一棵树,每条边上有一个字符,字符集大小只 ...
- codeforces 812E Sagheer and Apple Tree(思维、nim博弈)
codeforces 812E Sagheer and Apple Tree 题意 一棵带点权有根树,保证所有叶子节点到根的距离同奇偶. 每次可以选择一个点,把它的点权删除x,它的某个儿子的点权增加x ...
- codeforces 220 C. Game on Tree
题目链接 codeforces 220 C. Game on Tree 题解 对于 1节点一定要选的 发现对于每个节点,被覆盖切选中其节点的概率为祖先个数分之一,也就是深度分之一 代码 #includ ...
- Codeforces E. Alyona and a tree(二分树上差分)
题目描述: Alyona and a tree time limit per test 2 seconds memory limit per test 256 megabytes input stan ...
- Codeforces 379 F. New Year Tree
\(>Codeforces \space 379 F. New Year Tree<\) 题目大意 : 有一棵有 \(4\) 个节点个树,有连边 \((1,2) (1,3) (1,4)\) ...
- 【27.91%】【codeforces 734E】Anton and Tree
time limit per test3 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...
随机推荐
- Minimum Spanning Tree
前言 说到最小生成树(Minimum Spanning Tree),首先要对以下的图论概念有所了解. 图 图(Graph)是表示物件与物件之间的关系的数学对象,是图论的基本研究对象.图的定义方式有两种 ...
- bzoj3316 JC loves Mkk题解
3316: JC loves Mkk Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 979 Solved: 316[Submit][Status][Di ...
- WinForm控件之【DateTimePicker】
基本介绍 时间控件应用较为广泛,属性设置项也比较完善是非常好用的控件. 常设置属性.事件 CustomFormat:当Format属性设置为自定义类型时可自定义控件时间的显示格式: Enabled:指 ...
- Java编程思想:通配符(后面有两个小节,研究的不够深入)
import java.util.*; public class Test { public static void main(String[] args) { } } /* 15.9 边界 要点: ...
- .NET CORE 多语言实现方案
根据市场需求,基于.NET CORE平台开发的RoadFlow工作流平台需要实现多语言版本.经过方案讨论和比对,决定采用.NET自带的本地化功能来实现多语言.话不多说,直接上实现方式. 首先修改Sta ...
- 解决tensorflow模型保存时Saver报错:TypeError: TF_SessionRun_wrapper: expected all values in input dict to be ndarray
TypeError: TF_SessionRun_wrapper: expected all values in input dict to be ndarray 对于下面的实际代码: import ...
- ThinkPHP 入门
ThinkPHP是一个免费.开源的,快速.简单地面向对象的轻量级PHP开发框架,遵循Apache2开源协议发布,是为了敏捷Web应用开发和简化企业级应用开发而诞生的.ThinkPHP借鉴国外很多优秀的 ...
- Pinyin4j简单使用教程
Pinyin4j是一个流行的Java库,支持中文字符和拼音之间的转换,拼音输出格式可以定制,在项目中经常会遇到需求用户输入汉字后转换为拼音的场景,这时候Pinyin4j就可以派上用场 有自己私服的可以 ...
- 微信小程序设计总结
微信小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验. 小程序提供了一个简单.高效的应用开发框架和丰富的组件及API,帮助开发者在微信中开发具有原生 A ...
- Linux学习(一)--VMware下Linux安装和配置
本片随便将给大家讲述linux在VM虚拟机上安装及终端的安装和配置 一.Linux介绍 Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户.多任务.支持多线 ...