图(综合(1))

课程表 II

https://leetcode-cn.com/problems/course-schedule-ii/
思路:拓扑排序。将每一门课看成图的一个顶点,如果想要学习课程 1之前必须完成课程 0,那么可以构造从 0 到 1 的一条有向边。在拓扑排序中得到的序列中,0 一定出现在 1 的前面。因此构造所有课程的邻接表,求出拓扑序列,则可以得到
一种符合要求的课程学习顺序(拓扑序列不是唯一的)。
拓扑排序步骤可以参考王卓老师的PPT
在这里插入图片描述
具体实现:广度优先搜索。构造邻接表和入度数组,将入度为0的节点(即没有前驱的节点)放入队列中。每次从队头取出入度为0的节点 i 放入结果数组res当中,让 i 出队并且将 i 的相邻节点 j(i 指向 j)的入度数减去1,如果减去1之后入度数变为0,则把该节点 j 加入队列中。如此循环,直到队列为空为止。最后检查结果数组的顶点数是否和课程总数相等,如果不相等,说明图存在环,即无法实现修完所有课程。

class Solution {
public:
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        vector<vector<int>> graph(numCourses,vector<int>());//构建邻接表
        vector<int> degree(numCourses,0);//创建一个入度数组
        for(int i = 0;i<prerequisites.size();i++){
            graph[prerequisites[i][1]].push_back(prerequisites[i][0]);
            degree[prerequisites[i][0]]++;
        }
        queue<int> q;
        for(int i = 0;i<numCourses;i++){
            if(degree[i]==0){
                q.push(i);
            }
        }
        vector<int> res;
        while(!q.empty()){
            int temp = q.front();
            q.pop();
            res.push_back(temp);
            for(int i = 0;i<graph[temp].size();i++){
                if(--degree[graph[temp][i]]==0){
                    q.push(graph[temp][i]);
                }
            }
        }
        return (res.size()==numCourses)?res:vector<int>();
    }
};
Instrction Arrangement

http://acm.hdu.edu.cn/showproblem.php?pid=4109
思路:关键路径问题用拓扑排序+dp求解。用顶点表示事件,弧表示活动,所以只有当一个事件(级图的顶点)的入度为0 才能开始这个事件,如果一个顶点(设为A)的入度不为1,即n个点(假如多个点用X数组表示)分别用n条弧指向该顶点它,那么最早开始执行时间是 X[i] + W[i](W[i]为A到X活动的消耗时间) 里边的最大值 即当全部指向顶点A的事件和活动(点和弧) 做完了才能开始。因此利用dp数组存储节点i的最迟开始时间,对每个指向它的节点都判断,它的最迟开始时间是什么时候,最后比较所有节点的最迟开始时间,最大的就是完成所有活动所需的最短时间。

#include <iostream>
#include <climits>
#include <cstring>
#include <queue>
using namespace std;

struct node{
	int end;
	int w;
};
vector<node> graph[1005];
int degree[1005];
int dp[1005];//表示以i为终点的最长路径 
int n,m;

int main(){
	while(cin>>n>>m){
		memset(degree,0,sizeof(degree));
		memset(dp,0,sizeof(dp));
		for(int i = 0;i<n;i++){
			graph[i].clear();
		}
		int u,v,w;
		for(int i = 1;i<=m;i++){
			cin>>u>>v>>w;
			graph[u].push_back(node{v,w});
			degree[v]++;
		}
		//拓扑排序
		queue<int> q;
		for(int i = 0;i<n;i++){
			if(degree[i]==0){
				q.push(i);
				dp[i]++;
			}
		} 
		while(!q.empty()){
			int temp = q.front();
			q.pop();
			for(int i = 0;i<graph[temp].size();i++){
				int end = graph[temp][i].end;
				int w = graph[temp][i].w;
				dp[end] = max(dp[end],dp[temp]+w);
				if(--degree[end]==0){
					q.push(end);
				}
			}
		}
		int res = 0;
		for(int i = 0;i<n;i++){
			res = max(res,dp[i]);
		}
		cout<<res<<endl;
	}
} 
Borg Maze

http://poj.org/problem?id=3026
思路:BFS+最小生成树。把字母(A或S)看成图的节点,要求代价最小即为图的最小生成树问题。因为给出的迷宫还含有‘#’,所以可以用 BFS(深度优先搜索)求出任意两个节点之间的距离,构造图的邻接矩阵,进而利用Prime算法求解最小代价。

#include <iostream>
#include <climits>
#include <cstring>
#include <queue>
using namespace std;

int t,x,y;
int count;//字母的数量 
char graph[55][55];
int num[55][55];//num表示字母的编号 
int dis[55][55];//邻接矩阵 
int len[55][55];
int vis[55][55];//邻接矩阵的访问数组 

struct Point{
	int x,y;
};
int dx[4] = {0,0,-1,1};
int dy[4] = {1,-1,0,0};
void BFS(int row,int col){
	queue<Point> q;
	Point node;
	node.x = row;
	node.y = col;
	q.push(node);
	for(int i = 0;i<=y;i++){
		for(int j = 0;j<=x;j++){
			vis[i][j] = 0;
		}
	}
	vis[row][col] = 1;//标记已经被访问 
	len[row][col] = 0;
	while(!q.empty()){
		Point temp = q.front();
		q.pop();
		int x1 = temp.x;
		int y1 = temp.y;
		if(num[x1][y1]!=-1){
			dis[num[row][col]][num[x1][y1]] = len[x1][y1];//更新邻接矩阵 
		}
		//四个方向寻找 
		for(int k = 0;k<4;k++){
			int x2 = x1+dx[k];
			int y2 = y1+dy[k];
			if(x2>0 && y2>0 && x2<=y && y2<=x && !vis[x2][y2] &&graph[x2][y2-1]!='#'){
				vis[x2][y2] = true;//找到则标记已经访问过了 
				len[x2][y2] = len[x1][y1]+1;//距离加 1 
				Point New;
				New.x = x2;
				New.y = y2;
				q.push(New);//把新节点放到队列里面 
			}	
		}
	}
}
int Prime(int index){
	int d[55];
	bool v[55];
	memset(v,false,sizeof(v));//字母的访问数组 
	for(int i = 1;i<=count;i++){
		d[i] = dis[index][i]; 
	}
	v[index] = true;
	d[index] = 0;
	
	int cost = 0;
	//Prim算法
	for(int i = 1;i<=count-1;i++){
		int Min = INT_MAX;
		int val;
		for(int j = 1;j<=count;j++){
			if(!v[j]&&d[j]<Min){
				Min = d[j];
				val = j;
			}
		}
		v[val] = true;
		cost += Min;
		for(int j = 1;j<=count;j++){
			if(!v[j]&&d[j]>dis[val][j]){
				d[j] = dis[val][j];
			}
		}	
	} 
	return cost;	
} 
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&x,&y);
		char temp[100];
		gets(temp);//去掉空格 
		for(int i = 1;i<=y;i++){
			gets(graph[i]); //构造地图 
		}
		count = 0;
		memset(num,-1,sizeof(num)); 
		int index;
		for(int i = 1;i<=y;i++){
			for(int j = 0;j<x;j++){
				if(graph[i][j]=='S'||graph[i][j]=='A')
					num[i][j+1] = ++count;//对字母进行编号 
				if(graph[i][j]=='S')
					index = count;
			}
		}

		for(int i = 1;i<=y;i++){
			for(int j = 0;j<x;j++){
				if(graph[i][j]=='A'||graph[i][j]=='S')
					BFS(i,j+1);
			}
		}		
		
		printf("%d\n",Prime(index));
	}
	return 0;
}
Sorting It All Out

http://poj.org/problem?id=1094
思路:拓扑排序。输入m组关系,每输入一组都要进行一次拓扑排序。存在三种情况:存在环,有矛盾;可以进行拓扑排序且序列唯一;拓扑排序序列不唯一,所以无法判定。注意:如果找到确定顺序,只需要把剩余数据读完即可。存在不确定的顺序时,仍然需要判断是否存在环,如果存在环,则输出矛盾。

#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;

int n,m;
vector<int> graph[30];
vector<int>degree(26,0);
vector<int> arr(26);

int topo(){
	queue<int> q;
	vector<int> copy(26,0);
	for(int i = 0;i<n;i++){
		copy[i] = degree[i];
		if(copy[i]==0)
			q.push(i);
	}
	int k = 0;
	bool flag = false;//作为标志,判断是不是可以进行拓扑排序,并且只有唯一一种方式
	while(!q.empty()){
		if(q.size()!=1)//如果每次有多个点得入度为0则最终序列不唯一
			flag = true;
		int num = q.front();
		q.pop();
		arr[k++]=num;
		int len = graph[num].size();
		for(int i = 0;i<len;i++){
			if(--copy[graph[num][i]]==0)
				q.push(graph[num][i]);
		}
	}
	if(k<n)
		return -1;//不能判定 
	if(flag) //可以进行拓扑排序,有多种情况
		return 0;
	return 1;//只有唯一一种情况
}
int main(){
	int res = 0;
	while(scanf("%d%d",&n,&m) && n && m){
		string link;
		int flag1  = -1;
		int flag2 = -1;
		
		for(int j = 0;j<26;j++){
			degree[j] = 0;
		}
		for(int j = 0;j<30;j++){
			graph[j].clear();
		}
		for(int i = 0;i<m;i++){
			int left,right;
			cin>>link;
			
			left = link[0]-'A';
			right = link[2]-'A';
			
			graph[left].push_back(right);
			degree[right]++;
			
			if(flag1!=-1||flag2!=-1)
                continue;
                
        	int res = topo();
        	if(res==1){
        		flag1 = i+1;
			}else if(res==-1){
				flag2 = i+1;
			}
    	}
			
		if(flag2!=-1){
			printf("Inconsistency found after %d relations.\n",flag2);
		}
		else if(flag1 != -1){
			printf("Sorted sequence determined after %d relations: ",flag1);
			for (int i = 0; i < n; ++i)
            	putchar('A'+arr[i]);
        	printf(".\n");
		}
		else
        {
            printf("Sorted sequence cannot be determined.\n");
        }
	}
	return 0;
} 
```
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值