算法竞赛入门经典第十一章例题与代码(排序从前到后)

7 篇文章 0 订阅

无根树转有根树

输入一个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;

//

//}

 

//最小生成树和并查集一下都用一个安慰奶牛例子来说明:

 

//最小生成树的概念:一个有个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有个结点,并且有保持图连通的最少的边。

//最小生成树的详细说明: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条双向道路连接了牧场SjEj(1 <= Sj <= N; 1 <= Ej <= N; Sj != Ej),而且走完它需要Lj的时间。没有两个牧场是被一条以上的道路所连接。奶牛们非常伤心,因为她们的交通系统被削减了。你需要到每一个奶牛的住处去安慰她们。每次你到达第i个牧场的时候(即使你已经到过),你必须花去Ci的时间和奶牛交谈。你每个晚上都会在同一个牧场(这是供你选择的)过夜,直到奶牛们都从悲伤中缓过神来。在早上起来和晚上回去睡觉的时候,你都需要和在你睡觉的牧场的奶牛交谈一次。这样你才能完成你的交谈任务。假设Farmer John采纳了你的建议,请计算出使所有奶牛都被安慰的最少时间。

//

//输入格式

//第行包含两个整数NP

//

//接下来N行,每行包含一个整数Ci

//

//接下来P行,每行包含三个整数Sj, EjLj

//

//输出格式

//输出一个整数所需要的总时间(包含和在你所在的牧场的奶牛的两次谈话时间)

//这个人从早上从住处出发到晚上回到住处,完成一条从一个牧场把所有牧场走一遍回到这个牧场的路径,模拟着走一遍会发现

//对于每条边我们花费的时间是而被的边权值加上两个端点的权值。用这个花费做边权,求最小生成树,过夜的地方选择权值最小的点

//测试数据:

//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)//如果xfa[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,求从vG中其余各顶点的最短路径。迪杰斯特拉(Dijkstra)提出了按路径长度递增的顺序产生各顶点的最短路径算法。

//该算法的基本思想是:

//()设置两个顶点的集合STVS,集合S中存放已找到最短路径的顶点,集合T存放当前还未找到最短路径的顶点;

//()初始状态时,集合S中只包含源点v0

//()从集合T中选取到某个顶点vi(要求viv0的路径长度最小)加入到S中;

//()S中每加入一个顶点vi,都要修改顶点v0T中剩余顶点的最短路径长度值,它们的值为原来值与新值的较小者,新值是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;

//}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值