拓扑排序入门

最长路

给你一个有向无环图,N个点M条边

求最长路

Format
Input
第一行给出N,M,代表点数与边数

接下M行,每行3个数字a,b,c代表从a到b有边权为1

N,M<=1e5

Output
如题 ,如果没有的话,输出0.

否则输出结果%10^9+7

Samples
输入数据 1
4 5
1 2
1 3
3 2
2 4
3 4
输出数据 1
3

 

这个权值在边上

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
int n,m;
int x,y;
int dep[100010];
int son[2000010],now[2000010],pre[2000010],val[2000010],tot;
queue<int> q;
int ans;
void put(int x, int y) {
	pre[++tot] = now[x];
	now[x] = tot;
	son[tot] = y;
}
int main() {
	cin>>n>>m;
	//n个点,m条边 
	for(int i=1; i<=m; i++) 
	{
		cin>>x>>y;
		put(x,y);
		dep[y]++; 
		//y的入度加1 
	}
	for(int i=1; i<=n; i++)
		if(dep[i]==0)
		//将入度为0的点加入队列 
			q.push(i);
	tot=0;
	while(!q.empty()) 
	//当队列不为空 
	{
		int noww=q.front();
		//取出头结点 
		q.pop();
		tot++;
		for(int i=now[noww]; i; i=pre[i]) 
		//遍历与noww相连的边,找出这条边上的另一个点 
		{
			dep[son[i]]--;
			//son[i]这个点的入度要减少1 
			if(dep[son[i]]==0) 
			    q.push(son[i]);
			val[son[i]]=max(val[son[i]],val[noww]+1);
		}
	}
	if(tot!=n) 
	   cout<<0<<endl;
	else 
	{
		for(int i=1; i<=n; i++) 
		    ans=max(ans,val[i]);
		cout<<ans%mod<<endl;
	}

	return 0;
}

  

 

Test for Job

Mr.dog被他原来的公司炒了。现在为了养家糊口的他,要尽快找到一个工作。现在有一个工作,但是这年头失业的人很多啊,因此公司不得不使用一个测试来选择自己需要的职工。

这个测试是这样的:从一个起始的城市出发,通过单向的路径来到其他的城市,并且经过每个城市可能会赚到一定的利润,或者交纳一定的费用,这样不断的行动,直到到达一个终点的城市。你的老板会在终点处等你,他会总结出你一路上花费的费用,或者是得到的利润,来决定你是否能够被雇佣。

为了更好的得到这份工作,Mr.dog设法得到了所有城市经过的纯利润Vi(如果是负数表示到这个城市不但得不到利润,还要花费),以及城市与城市的连通关系。假设一个城市没有能够从其他城市到达的道路,那么这个城市被看作起始的城市之一;假设一个城市没有能够到达其他城市的道路,那么这个城市被看作终点的城市之一;而Mr.dog的任务是将选择一个起始的结点出发,走出一条路径,这条路径在一个终点的城市结束,且这条路径包含的城市的总利润最大。

Input
包含若干个数据。

每个数据的第一行有2个整数n,m(1 < = n < = 100000,1 < =m < = 1000000)

后面紧接着n行,其中第i行描述Vi

再后面紧接m行,第i行2个整数x,y,表示有一条从x到y的单向道路。

数据保证没有点对(x,y)重复出现,并且保证无法从一个城市出发经过若干条路径后再回到这个城市。

Output
只有一行,一个整数表示dog这次测试最多可以得到多少利润(或者说最少使用多少花费)

Samples
输入数据 1
6 5
1
2
5
2
1
1
1 2
1 3
2 4
3 4
5 6
输出数据 1
8

Hint
请用 while(scanf("%d%d",&n,&m)!=EOF)进行读入

 

 

 这个权值在点上

#include<bits/stdc++.h>
using namespace std;
int n,m;
int x,y;
int dep[2000010],out[2000010],val[2000010],ans[2000010];
int son[2000010],now[2000010],pre[2000010],tot;
queue<int> q;
int max1;
void put(int x, int y) {
	pre[++tot] = now[x];
	now[x] = tot;
	son[tot] = y;
}
int main() {
	while(scanf("%d%d",&n,&m)!=EOF) {
		memset(dep,0,sizeof dep);
		memset(son,0,sizeof son);
		memset(now,0,sizeof now);
		memset(pre,0,sizeof pre);
		memset(val,0,sizeof val);
		memset(ans,0,sizeof ans);
		memset(out,0,sizeof out);
		tot=0;
		for(int i=1; i<=n; i++) cin>>val[i],ans[i]=-INT_MAX;
		for(int i=1; i<=m; i++) {
			cin>>x>>y;
			put(x,y);
			dep[y]++;
			out[x]++;
		}
		for(int i=1; i<=n; i++)
			if(dep[i]==0)
				q.push(i),ans[i]=val[i];
		while(!q.empty()) {
			int noww=q.front();
			q.pop();
			for(int i=now[noww]; i; i=pre[i]) {
				dep[son[i]]--;
				if(dep[son[i]]==0) 
				    q.push(son[i]);
				ans[son[i]]=max(ans[son[i]],ans[noww]+val[son[i]]);
				//已找到一条路径了,于是更新下最值优
				//这个与是否入度为0,是没有关系的 
			}
		}
		max1=-INT_MAX;
		for(int i=1; i<=n; i++)
			if(out[i]==0)
//找到出度为0的点,其代表终点 max1=max(max1,ans[i]); cout<<max1<<endl; } return 0; }

  

 

有限制的全排列

给你一个数字N,及另一个数字M

代表希望你找出N的某个全排列出来,其满足M个条件

每个条件会给出两个数字a,b

代表在目标全排列中,数字a在数字b前面

Format
Input
第一行两个整数n,m

接下来M行,每行两个数字

Output
输出目标全排列,找不到输出-1

如果存在多个解则输出字典序最小的那个

Samples
输入数据 1
4 3
2 1
3 4
2 4
输出数据 1
2 1 3 4

 

#include<bits/stdc++.h>
using namespace std;
int n,m;
int x,y;
int dep[2000010];
int son[2000010],now[2000010],pre[2000010],tot;
priority_queue<int, vector<int> , greater<int> > q;
queue<int> ans;
void put(int x, int y) {
	pre[++tot] = now[x];
	now[x] = tot;
	son[tot] = y;
}
int main() {
	cin>>n>>m;
	for(int i=1; i<=m; i++) {
		cin>>x>>y;
		put(x,y);
		dep[y]++;
	}
	for(int i=1; i<=n; i++)
		if(dep[i]==0)
			q.push(i);
	while(!q.empty()) {
		int noww=q.top();
		q.pop();
		ans.push(noww);
		for(int i=now[noww]; i; i=pre[i]) {
			dep[son[i]]--;
			if(dep[son[i]]==0) q.push(son[i]);
		}
	}
	if(ans.size()!=n) cout<<-1<<endl;
	else {
		while(!ans.empty()) cout<<ans.front()<<" ",ans.pop();
		cout<<endl;
	}

	return 0;
}

  

 

有限制的全排列

 

给你一个数字N,及另一个数字M

 

代表希望你找出N的某个全排列出来,其满足M个条件

 

每个条件会给出两个数字a,b

 

代表在目标全排列中,数字a在数字b前面

 

Format

Input

第一行两个整数n,m

 

接下来M行,每行两个数字

 

Output

输出目标全排列,找不到输出-1

 

如果存在多个解则输出字典序最小的那个

 

Samples

输入数据 1

4 3

2 1

3 4

2 4

输出数据 1

2 1 3 4

 

sol:

针对形如

a要放在b的前面的表述,建立一条有向表从a指向b

 

然后找出所有入度的0的点,放入一个小根堆中(注意不是一般的队列)

 

然后不断从堆中弹出堆顶元素x,其权值最小,并且是比所有数字小的。

 

然后将所有与x相连的边所指向的另一个点的入度减少1,如果入度为0,则又放到小堆根中,最终得到答案

 

针对样例来说:

2 1

3 4

2 4

 

建立的图形如下:

 

 

 

 

 

 

 

 

首先将入度为0的点2,3入堆中

 

然后弹出2出,并将1与4的入度分别减少1。发现1的入度为0,于是1进入堆中

 

然后弹出1,1没有相连的边,无后续操作

 

然后弹出3,将4的入度减少1,发现4的入度为0,加入堆中

 

最后弹出4

 

最终结果为2,1,3,4

 

#include<bits/stdc++.h>
using namespace std;
int n,m;
int x,y;
int dep[2000010];
int son[2000010],now[2000010],pre[2000010],tot;
priority_queue<int, vector<int> , greater<int> > q;
queue<int> ans;
void put(int x, int y) {
	pre[++tot] = now[x];
	now[x] = tot;
	son[tot] = y;
}
int main() {
	cin>>n>>m;
	for(int i=1; i<=m; i++) {
		cin>>x>>y;
		put(x,y);
		dep[y]++;
	}
	for(int i=1; i<=n; i++)
		if(dep[i]==0)
			q.push(i);
	while(!q.empty()) {
		int noww=q.top();
		q.pop();
		ans.push(noww);
		for(int i=now[noww]; i; i=pre[i]) {
			dep[son[i]]--;
			if(dep[son[i]]==0) q.push(son[i]);
		}
	}
	if(ans.size()!=n) cout<<-1<<endl;
	else {
		while(!ans.empty()) cout<<ans.front()<<" ",ans.pop();
		cout<<endl;
	}

	return 0;
}

  

 

 

小球的标签

 

有N个球,重量从1到N,各不相同,每个球有个编号也是从1到N,各不相同。现在给你一些约束条件, 每个约束条件给出数字A,B,表示A号球轻于B号球。请你求出满足约束条件的某个球的重量的全排列, 注意如果有多个排列满足条件,我们希望1号球的重量越小越好;当1号球的重量一样时,希望2号球的重量越小越好 当2号球的重量一样时,希望3号球的重量越小越好

 

Input
第一行给出一个数字N,代表有N组数据

 

每个数据给出数字N,M,代表有N个球,

 

M个约束条件N (1 ≤ N ≤ 200) , M (0 ≤ M ≤ 40,000).

 

下面将有M行每行两个数字A,B,(1 ≤ a, b ≤ N)表示A号球轻于B号球.

 

Output
依次输出从1号球到N号球的重量,任两个数字之间以一个空格格开

 

无解输出-1

 

Sol:
本题要求最终结果字典序最小,有两种策略
1:将较轻的重量给数字编号较小的球
2:将较重的重量给数字编号较大的球

 

发现策略2优于策略1,例如下面这组数据
4 2
3 1
2 4
我们建立有向边a-->b,代表a重于b,上图形如下

 

 

 

 


将1与4进大根堆

 

弹出4,将最大的重量4给它,然后将2加入堆

 

弹出2,将重量3给它

 

弹出1,将重量2给它,然后3加入堆

 

最后弹出3,将重量1给它。

 

最终1,2,3,4这四个球的重量分别为2 3 1 4

 

 

大家可试用着策略1来做一下,发现结果并不优于这种

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int head[500],degree[500],result[450],m,n,t;
struct node {
    int to,next;
}s[400000];
void add(int x,int y)
{
    s[t].to=y;
    s[t].next=head[x];
    head[x]=t++;
}
int topo()
{
    int i,j,p;
    priority_queue<int >Q;
    for(i=1;i<=m;i++)
    if(degree[i]==0)
    Q.push(i);
    p=m;
    while(!Q.empty())
    {
        int w=Q.top();
        Q.pop();
        result[w]=p--;
         
        for(int k=head[w];k!=-1;k=s[k].next)
        {
            int e=s[k].to;
            degree[e]--;
             
            if(!degree[e])
            Q.push(e);
        }
    }
    if(p==0)
    return 1;
     
    return 0;
}
int main()
{
    int N,a,b,i,j;
    scanf("%d",&N);
    while(N--)
    {
        t=0;
        memset(degree,0,sizeof(degree));
        memset(head,-1,sizeof(head));
        scanf("%d%d",&m,&n);
        for(i=0;i<n;i++)
        {
            scanf("%d%d",&a,&b);
            add(b,a);
            degree[a]++;
        }
        int qw=topo();
        if(qw)
        {
            for(i=1;i<m;i++)
            printf("%d ",result[i]);
            printf("%d\n",result[i]);
        }
        else
        printf("-1\n");
    }
    return 0;
}

  

 

posted @ 2022-06-18 10:26  我微笑不代表我快乐  阅读(41)  评论(0编辑  收藏  举报