无根树转有根树
输入一个n个借点的无根树的个条边,并指定一个根节点,要求把该树转化为有根树,
输出个个结点的父亲编号。
//#include<iostream>
//#include<vector>
//#define maxn 100
//using namespace std;
//vector<int> G[maxn];
//
//int p[maxn];//保存每一个节点的父节点
//void read_tree()
//{
//int u,v;
//int n;
//cin>>n;
//for(int i=0;i<n;i++)
//{
//cin>>u>>v;
//G[u].push_back(v);
//G[v].push_back(u);
//}
//}
//
//void dfs(int u,int fa)
//{
//int d=G[u].size();//与u点相连的点的个数
//for(int i=0;i<d;i++)
//{
//int v=G[u][i];
//if(v!=fa)//当v链接的点不是父节点的时候,
//{
// dfs(v,p[v]=u);//继续进行以u为父节点的递归,
// cout<<v<<"的父节点是:"<<u<<endl;//当if条件不成立的时候,最里层递归会跳出打印这句话
//}
//}
//}
//int main()
//{
//memset(p,-1,sizeof(p));//将数组中的元素初始化为-1
//read_tree();
//dfs(1,-1);//递归调用dfs,初次调用时表示根结点,-1表示结点的无父结点
//return 0;
//}
表达式树:
二叉树是表达式的常用工具,将一个表达式表示成二叉树
算法分析:首先是如何建树,根据提上所说,可知,最后运算的运算符肯定是树根,比如下面这个个表达式:
a+b*(c-d)-e/f。往往最先计算的所在树的深度越深,最晚计算的里树根越近,这个地方,建的树是:
// // -
//// + /
// // a * e f
// // b -
// // c d
//
//#include<iostream>
//#include<cstdlib>
//using namespace std;
//
//#define maxn 1000
//
//int l[maxn];//每个结点的左儿子
//int r[maxn];//每个结点的右儿子
//char op[maxn];//存放每个结点的字符
//int nc=0;//nc存放表达式树的结点,开始时结点数为
//
//
//int build_tree(char *s,int x,int y)//s是表达式,x是表达式的头下标,y是表达式的尾下标+1
//{
//
//int p=0,c1=-1,c2=-1;
//int u;
//
//if(y-x==1)//说明已经计算到最后一个字符了(y是)
//{
//u = nc++;
//l[u]=r[u]=0;
//op[u]=s[x];
//return u;
//}
//
//for(int i=x;i<y;i++)
//{
//switch(s[i])
//{
//case '(':p++;break;
//case ')':p--;break;
//case '+':case '-':if(!p)c1=i;break;//当是+-符号时,如果!p为真即+-符号不在括号内c1等于此运算符的下标,(最后一个运算符的下标)
//case '*':case '/':if(!p)c2=i;break;//当是*/符号时,如果!p为真即*/符号不在括号内c2等于此运算符的下标,(最后一个运算符的下标)
//}
//}
//if(c1<0)//表示没有+-号,或者括号外没有加减号
//c1=c2;//将乘除号赋给c1
//if(c1<0)//如果上面的if不成立,下面的必然不成立,如果c1现在等于c2还是小于,表示整个表达式被一个大括号扩了起来。
// return build_tree(s,x+1,y-1);//去掉最外面的大括号继续递归
//
//u=nc++;//在递归建树之前,先见记录子树的数组下标+1;
//l[u]=build_tree(s,x,c1);//递归建立左子树
// r[u]=build_tree(s,c1+1,y);//递归建立右子树
// op[u]=s[c1];//赋值符号
//
//return u;
//}
//
//int trans_tree(int cur)
//{
//if(l[cur]==0||r[cur]==0)//当前节点无左右子树
//{
//return op[cur]-'0';//op数组中存储的字符减去‘’;
//}
//else
//{
//int ret1=trans_tree(l[cur]);
//int ret2=trans_tree(r[cur]);
//switch(op[cur])
//{
//case '+':return ret1+ret2;
//case '-':return ret1-ret2;
//case '*':return ret1*ret2;
//case '/':return ret1/ret2;
//}
//}
//}
//
//int main()
//{
//char s[100];
//memset(s,0,sizeof(s));//初始化数组
//cin>>s;
//int len=strlen(s);
//build_tree(s,0,len);
// cout<<trans_tree(0);
//return 0;
//
//}
//最小生成树和并查集一下都用一个安慰奶牛例子来说明:
//最小生成树的概念:一个有n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有n 个结点,并且有保持图连通的最少的边。
//最小生成树的详细说明:http://baike.baidu.com/link?url=aBff-kn5d95CHEH8LapcfRUoH7co3D7djjo0pDGopZi8yztpEN3GFLtkQA7GEOKc
//算法分析:到这个网址查看最小生成树的画法,http://baike.baidu.com/picview/288214/288214/0/eac4b74543a982265ca7756a8b82b9014a90eb1d.html#albumindex=0&picindex=0
//到这个网址查看输入输出的解释:http://acmpj.zstu.edu.cn/JudgeOnline/showproblem?problem_id=2873
//其实这个也可以看并查集
//
//问题描述
//Farmer John变得非常懒,他不想再继续维护供奶牛之间供通行的道路。道路被用来连接N个牧场,牧场被连续地编号为到N。每一个牧场都是一个奶牛的家。FJ计划除去P条道路中尽可能多的道路,但是还要保持牧场之间的连通性。你首先要决定那些道路是需要保留的N-1条道路。第j条双向道路连接了牧场Sj和Ej(1 <= Sj <= N; 1 <= Ej <= N; Sj != Ej),而且走完它需要Lj的时间。没有两个牧场是被一条以上的道路所连接。奶牛们非常伤心,因为她们的交通系统被削减了。你需要到每一个奶牛的住处去安慰她们。每次你到达第i个牧场的时候(即使你已经到过),你必须花去Ci的时间和奶牛交谈。你每个晚上都会在同一个牧场(这是供你选择的)过夜,直到奶牛们都从悲伤中缓过神来。在早上起来和晚上回去睡觉的时候,你都需要和在你睡觉的牧场的奶牛交谈一次。这样你才能完成你的交谈任务。假设Farmer John采纳了你的建议,请计算出使所有奶牛都被安慰的最少时间。
//
//输入格式
//第行包含两个整数N和P。
//
//接下来N行,每行包含一个整数Ci。
//
//接下来P行,每行包含三个整数Sj, Ej和Lj。
//
//输出格式
//输出一个整数, 所需要的总时间(包含和在你所在的牧场的奶牛的两次谈话时间)。
//这个人从早上从住处出发到晚上回到住处,完成一条从一个牧场把所有牧场走一遍回到这个牧场的路径,模拟着走一遍会发现
//对于每条边我们花费的时间是而被的边权值加上两个端点的权值。用这个花费做边权,求最小生成树,过夜的地方选择权值最小的点
//测试数据:
//5 7
//10
//10
//20
//6
//30
//1 2 5
//2 3 5
//2 4 12
//3 4 17
//2 5 15
//3 5 6
//4 5 12
//输出:
//#include <iostream>
//#include <cstdio>
//#include <algorithm>
//
//#define MAXN 111111
//#define INF 100000007
//using namespace std;
//int n, m;
//struct EDGE
//{
// int u, v;
// int len;
//}edge[MAXN];
//int fa[MAXN];
//int c[MAXN];
//bool cmp(EDGE x, EDGE y)
//{
// return x.len < y.len;
//}
//int find(int x)//寻找对应的节点
//{
// if(fa[x] == x)//如果x和fa[x]相同表示这个节点没有走过
// return x;
// int t = find(fa[x]);//如果上面不相等,表明走过了。继续找没有走过的。
// fa[x] = t;
//
// return t;
//}
//int main()
//{
// int x, y, z;
// scanf("%d%d", &n, &m);
// int mi = INF;
// for(int i = 1; i <= n; i++)
// {
// scanf("%d", &c[i]);
// mi = min(mi, c[i]);
// fa[i] = i;
// }
// for(int i = 1; i <= m; i++)
// {
// scanf("%d%d%d", &x, &y, &z);
// edge[i].u = x;
// edge[i].v = y;
// edge[i].len = z * 2 + c[x] + c[y];
// }
// sort(edge + 1, edge + m + 1, cmp);
//
// int sum = 0;
// for(int i = 1; i <= m; i++)
// {
// //这段代码的意思是找到一个点,如果可以连通就将后一个点标记成这个点的父节点,这就是最小生成树
// int fx = find(edge[i].u);//找到edge[i].u对应的父节点
// int fy = find(edge[i].v);//找到edge[i].v对应的父节点
// if(fx != fy)//如果这两个父节点不相同,表明是不在一个并查集内。
// {
// sum += edge[i].len;//加上所走的路径
// fa[fx] = fy;//将下标为fx的父节点改成fy,
// }
// printf("%d\n", sum + mi);
// return 0;
//#include<iostream>
//#include<algorithm>
//#define maxn 10000
//using namespace std;
//int p[maxn];
//struct node
//{
//int v;//v节点
//int u;//u节点
//int len;//节点直接的距离
//};
//
//
//node G[maxn];
//int find(int x)
//{
//return p[x]==x?x:find(p[x]);
//}
//
//int cmp(node a,node b)
//{
//return a.len<b.len;
//}
//
//int main()
//{
//int n,m;//n是节点的数量。m是边的数量
//int sum=0;//记录最小边
//cin>>n>>m;
//for(int i=0;i<n;i++)
//{
//p[i]=i;;
//}
//for(int i=0;i<m;i++)
//{
//cin>>G[i].u>>G[i].v>>G[i].len;//分别输入u点,v点及两点之间的距离
//
//}
//sort(G,G+m,cmp);//给结构体排序
//
//for(int i=0;i<n;i++)
//{
//int fx=find(G[i].u);
//int fy=find(G[i].v);
//if(fx!=fy)
//{
//sum+=G[i].len;
//cout<<"链接"<<G[i].u<<"和"<<G[i].v<<endl;
//p[fx]=fy;
//}
//}
//cout<<sum<<endl;
//return 0;
//}
//从某个源点到其余各顶点的最短路径,即单源点最短路径。单源点最短路径是指:给定带权有向图G和源点v,求从v到G中其余各顶点的最短路径。迪杰斯特拉(Dijkstra)提出了按路径长度递增的顺序产生各顶点的最短路径算法。
//该算法的基本思想是:
//()设置两个顶点的集合S和T=V-S,集合S中存放已找到最短路径的顶点,集合T存放当前还未找到最短路径的顶点;
//()初始状态时,集合S中只包含源点v0;
//()从集合T中选取到某个顶点vi(要求vi到v0的路径长度最小)加入到S中;
//()S中每加入一个顶点vi,都要修改顶点v0到T中剩余顶点的最短路径长度值,它们的值为原来值与新值的较小者,新值是vi的最短路径长度加上vi到该顶点的路径长度;
//()不断重复()和(),直到S包含全部顶点。
//输入数据:
//5 7
//1 2 10
//1 4 30
//1 5 100
//2 3 50
//3 5 10
//4 3 20
//4 5 60
//输出数据:
//0
//10
//50
//30
//60
//#include <stdio.h>
//#include <string.h>
//const int INF = 1000000000;
//const int MAXN = 1000;
//int n, m;
//int v[MAXN], d[MAXN], G[MAXN][MAXN];//G是邻接矩阵存储图
//int main() {
//scanf("%d%d", &n, &m);
//for(int i = 1; i <=n; i++)
//for(int j = 1; j <=n; j++)
//G[i][j] = INF;//先初始化为无穷大
//
//for(int i = 1; i <=m; i++)
//{
//int u, v, w;
//scanf("%d%d%d", &u, &v, &w);//输入节点和边权值
//G[u][v] = w;
//}
//memset(v, 0, sizeof(v));//v是是否访问过的标记
//
//for(int i = 1; i <=n; i++)
//d[i] = (i==1 ? 0 : INF);//刚开始定义到的距离是,到其他的距离是无穷大
//
//for(int i = 1; i <=n; i++)
//{
//int x=0, m = INF;
//for(int y = 1; y <=n; y++)
//{
//if(!v[y]&&d[y]<=m)
//{
//m=d[y];
//x=y;
//}
//}
//v[x] = 1;
//for(int y = 1; y <=n; y++)
//if(d[y]>d[x] + G[x][y])
//{
//d[y]=d[x] + G[x][y];
//}
//
//}
//for(int i = 1; i <=n; i++)
//printf("%d\n", d[i]);
//return 0;
//}
//#include<iostream>
//#define maxn 100000;
//int G[100][100];
//using namespace std;
//int main()
//{
//int d[100];//保存点到其他点的最短距离
//int vis[100];//标记节点是否被访问过
//int n,m;//n代表节点,m代表边
//cin>>n>>m;
////对邻接矩阵初始化
//for(int i=1;i<=n;i++)
//for(int j=1;j<=m;j++)
//G[i][j]=maxn;
// //对标记数组和距离数组赋值
//for(int i=1;i<=n;i++)
//{
//if(i==1)d[i]=0;
//else d[i]=maxn;
//vis[i]=0;
//}
//int u,v,w;
//for(int i=1;i<=m;i++)
//{
//cin>>u>>v>>w;
//G[u][v]=w;
//}
//
//for(int i=1;i<=n;i++)
//{
//int min=maxn;
//int x=0;
////在所有未标记的节点中寻找d值最小的节点
//for(int j=1;j<=n;j++)
//if(!vis[j]&&d[j]<=min)
//{
//min=d[j];
//x=j;
//}
//vis[x]=1;//标记此时d值最小的节点为访问过
//
////对于所有从x节点出发的边,更新d[y]=min(d[y],d[x]+G[x][j]);
//for(int j=1;j<=m;j++)
//{
//if(d[j]>d[x]+G[x][j])
//d[j]=d[x]+G[x][j];
//}
//}
//
//for(int i=1;i<=n;i++)
//cout<<d[i]<<endl;
//return 0;
//}
//用邻接矩阵表示边的值所在的内存空间,以及时间复杂度都太大了,下面想到一种用数组模拟链表的方法
#include<iostream>
#define maxn 1000
using namespace std;
int main()
{
int n,m;
cin>>n>>m;
int d[maxn];
int vis[maxn];
int u[maxn],v[maxn],w[maxn];
int first[maxn];//表头
int next[maxn];//链表指针
//对距离数组和标记数组初始化
for(int i=1;i<=n;i++)
{
if(i==1)
d[i]=0;
else d[i]=maxn;
vis[i]=0;
}
for(int j=1;j<=m;j++)
{
first[j]=-1;//初始化表头
}
//用数组模拟邻接表输入数据
for(int j=1;j<=m;j++)
{
cin>>u[j]>>v[j]>>w[j];
next[j]=first[u[j]];//从链表表头插入(first[]记录表头输在的下标值,而next是相连的,当前next的值,是下一个next的参数这样当指针)
first[u[j]]=j;
}
for(int i=1;i<=n;i++)
{
int x=0,min=maxn;
for(int j=1;j<=n;j++)
{
if(!vis[i]&&d[j]<=min)
{
m=d[j];x=j;
}
}
vis[x]=1;
for(int j=first[x];j!=-1;j=next[j])
{
if(d[j]>d[x]+w[j])
{
d[j]=d[x]+w[j];
}
}
}
for(int i=1;i<=n;i++)
cout<<d[i]<<endl;
return 0;
}
//fun(STREC *a,STREC *b)
//{
//int min=a[0].s;
//int count=0;
////遍历一遍,找到最小值
//for(int i=0;i<N;i++)
//{
//if(min>a[j].s)
//{
//min=a[j].s;
//}
//}
//for(int i=0;i<N;i++)
//{
//if(min==a[i].s)
//{
//b[count++]=a[i];
//}
//}
//
//return count;
//}
C++的STL中提供了“优先队列”这一容器,可以快速完成这一操作。优先队列和普通的FIFO队列都定义在<queue>中,有push()和pop()过程,分别表示“往队列里加入新元素”和“从队列里删除队首元素”。唯一的区别是,在优先队列中,元素并不是按照进入队列的先后顺序排列,而是按照优先级的高低顺序排列。即pop()删除的是优先级最高的元素,而不一定是最先进入队列的元素。正因为如此,获取队首元素的方法不再是front(),而是pop()。
定义优先队列最简单方法是priority_queue<类型名>q,它利用元素自身的“小于”操作符来定义优先级。例如,在priority_queue<int> q这样的优先队列中,先出队的总是最大的整数。使用自定义比较的方法和set类似:
sturct cmp {
bool operator()(const int a,const int b) //a的优先级比b小时返回true
return a % 10 > b % 10;
}
};
priority_queue<int,vector<int>,cmp> q; //“个位数大的优先级反而小”的整数优先队列
在Dijsktra算法中,d[i]小的值应该先出队,因此需要使用自定义比较器。在STL中,可以用greater<int>表示“大于”运算符,因此可以用priority_queue<int,vector<int>,
greater<int> > q表声明一个小整数先出队的优先队列。注意,最后两个大于号之间一定要有空格,不然会被误认为移位运算符“>>”。
由于除了需要最小的d值之外,还要找到这个最小值对应的结点编号。解决方法是:把d值和编号“捆绑”成一个整体放到优先队列中,使得取出最小d值的同时也会取出对应的结点编号。
//#include <cstdio>
//#include <cstring>
//#include <queue>
//using namespace std;
//const int INF = 1000000000;
//const int MAXN = 1000;
//const int MAXM = 100000;
//int n, m;
//int first[MAXN], d[MAXN];
//int u[MAXM], v[MAXM], w[MAXM], next[MAXM];
//typedef pair<int,int> pii;//自定义的pii类型
//
//int main() {
// scanf("%d%d", &n, &m);
// for(int i = 0; i < n; i++) first[i] = -1;//邻接表,初始化表头
//
// for(int e = 0; e < m; e++) {//输入数据用临界表表示
// scanf("%d%d%d", &u[e], &v[e], &w[e]);
// next[e] = first[u[e]];//从链表头部插入节点
// first[u[e]] = e;
// }
// priority_queue<pii, vector<pii>, greater<pii> > q;//申请一个优先队列
//
//bool done[MAXN];
//for(int i = 0; i < n; i++)
//d[i] = (i==0 ? 0 : INF);
//
//memset(done, 0, sizeof(done)); //初始化计算标志:所有结点都没有算出
//
//q.push(make_pair(d[0], 0)); //起点进入优先队列(数据和节点)
//
//while(!q.empty()) {
// pii u = q.top();
//q.pop();
// int x = u.second;//x等于节点
//
//if(done[x]) continue; //已经计算过,忽略
//
//done[x] = true;
//
//for(int e = first[x]; e != -1; e = next[e])
// if(d[v[e]] > d[x]+w[e]) {
// d[v[e]] = d[x] + w[e]; //松弛成功,更新d[v[e]]
// q.push(make_pair(d[v[e]], v[e])); //加入优先队列
// }
//}
// for(int i = 0; i < n; i++)
// printf("%d\n", d[i]);
// return 0;
//}