Title

dijkstra算法+堆优化 + 链式前向星版本

dijkstra算法+堆优化 + 链式前向星版本

堆优化版本简单思路理清

typedef pair一下 PII

邻接矩阵、邻接表或链式前向星add一下来建图

void dijkstra(int s){
     小根堆走起
     给dist数组都赋值为无穷大(memset一下),
     让起点拥有一个表现的机会(赋值为0,且压入小根堆里面,push(PII(0,s)))first为距离,second为位置
     while一下,直到无路可走或者通关
     {
          提取当前距离到起点最短的点
          现提取出来的点的距离可能被上一个点给松弛掉了,直接continue(因为压入的是pair对,距离是当时的距离,不是最近更新的距离,而如果以原本的距离来对往后相连接的点再做一次松弛操作,将不具有最优的最短的距离,应该选择等待新松弛掉的这个点的新数据来对往后的点进行更新)
          
          for循环一下,将所连接的点都进行一次更新
          如果有所松弛,松弛掉的点就存在可能能够对其后续所连接的点的距离松弛的可能,因而就有必要将这个被当前结点松弛掉的点加入小根堆里面。
     }
     
}

代码

int h[N],ne[M],w[M],e[M],idx=0;//ne和e都表示的是边,h表示的是点   
void add(int a,int b,int c)
{
	e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
typedef pair<int,int > PII;
int dist[N];
int dijkstra(int st,int ed)
{
	priority_queue<PII,vector<PII>,greater<PII> > pq;
	memset(dist,0x3f,sizeof(dist));
	
	pq.push(PII(0,st));
	dist[st]=0;
	while(pq.size())
	{
		PII t = pq.top();
		pq.pop();
		int d = t.first,u = t.second;
		if(d>dist[u])continue;
		
		for(int i = h[u];~i;i=ne[i])
		{
			int j = e[i];
			if(dist[j]>d+w[i])
			{
				dist[j]=d+w[i];
				pq.push(PII(dist[j],j));
			}
		}
	}
	if(dist[ed]==0x3f3f3f3f) return -1;
	else return dist[ed];
}

单源最短路的建图方式

AcWing903. 昂贵的聘礼

年轻的探险家来到了一个印第安部落里。在那里他和酋长的女儿相爱了,于是便向酋长去求亲。酋长要他用10000 1000010000个金币作为聘礼才答应把女儿嫁给他。探险家拿不出这么多金币,便请求酋长降低要求。酋长说:”嗯,如果你能够替我弄到大祭司的皮袄,我可以只要8000 80008000金币。如果你能够弄来他的水晶球,那么只要5000金币就行了。”探险家就跑到大祭司那里,向他要求皮袄或水晶球,大祭司要他用金币来换,或者替他弄来其他的东西,他可以降低价格。探险家于是又跑到其他地方,其他人也提出了类似的要求,或者直接用金币换,或者找到其他东西就可以降低价格。不过探险家没必要用多样东西去换一样东西,因为不会得到更低的价格。探险家现在很需要你的帮忙,让他用最少的金币娶到自己的心上人。另外他要告诉你的是,在这个部落里,等级观念十分森严。地位差距超过一定限制的两个人之间不会进行任何形式的直接接触,包括交易。他是一个外来人,所以可以不受这些限制。但是如果他和某个地位较低的人进行了交易,地位较高的的人不会再和他交易,他们认为这样等于是间接接触,反过来也一样。因此你需要在考虑所有的情况以后给他提供一个最好的方案。为了方便起见,我们把所有的物品从1开始进行编号,酋长的允诺也看作一个物品,并且编号总是1。每个物品都有对应的价格P,主人的地位等级L LL,以及一系列的替代品T i和该替代品所对应的”优惠”V i

。如果两人地位等级差距超过了M MM,就不能”间接交易”。你必须根据这些数据来计算出探险家最少需要多少金币才能娶到酋长的女儿。

输入格式:
输入第一行是两个整数M,N,依次表示地位等级差距限制和物品的总数。接下来按照编号从小到大依次给出了N个物品的描述。每个物品的描述开头是三个非负整数P、L、X ,依次表示该物品的价格、主人的地位等级和替代品总数。接下来X XX行每行包括两个整数T和V,分别表示替代品的编号和”优惠价格”。

输出格式:
输出最少需要的金币数。

数据范围:
1 ≤ N ≤ 100
1 ≤ P ≤ 10000
1 ≤ L , M ≤ N
0 ≤ X < N
————————————————
参考博客:https://blog.csdn.net/qq_46105170/article/details/115121657

  • 国王可能只是许可等级差范围的一个点,在极端条件下,国王的等级可作为范围的最小值(左端点),而此时范围的最大值为国王的等级加上许可范围差,此外,国王的等级也可作为范围的最大值(右端点),而此时

  • 特殊操作:增设0点,反向建边

  • 应用:所有的其他点跑到起始点的最短距离(求单点)

    通过增设0点来连接所有其他点并且反向建边的操作我们可以实现保留产品原价的路径(最后返回dist[1],所有其他点跑到原本的起始点1)。(原本想法是将上一级的优惠不断传给当前的点,且当当前的点没有出度的时候,再加上当前点所对应的商品的价格,然后在比对数据点的时候才发现,有点有可能都是联通的(比如A可以令商人向B处先买点B的商品,B可令商人向C处买点C的商品;A可以令商人向C处先买点C的商品,C可令商人向B处买点B的商品))

#include<bits/stdc++.h>
using namespace std;
const int N=1E2+15,M=1E4+15,INF=0x3f3f3f3f;
int m,n;
int price[N],status[N];
int e[M],ne[M],w[M],h[N],idx=0;
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

int dist[N];
typedef pair<int,int > PII; 
int dijkstra(int l,int r)
{	
    int ans = INF;
	priority_queue<PII,vector<PII>,greater<PII> > pq;
	memset(dist,0x3f,sizeof(dist));
	dist[0]=0;
	pq.push(PII(0,0));
	
	while(pq.size())
	{
		PII t = pq.top();
		pq.pop();
		int d = t.first, u = t.second;
		if(d>dist[u]) continue;
		for(int i = h[u];~i;i=ne[i])
		{
			int j = e[i];
			if(status[j]<=r&&status[j]>=l&&dist[j]>d+w[i])
			{
				dist[j]=d+w[i];	
				pq.push(PII(dist[j],j)); 
			}
		}
	}
	return dist[1];
}

void init()
{
	memset(h,-1,sizeof(h));
}

int main()
{
	init();

	cin>>m>>n; 
	for(int i=1;i<=n;i++)
	{
		int alter;
		cin>>price[i]>>status[i]>>alter;
		add(0,i,price[i]);
		for(int j=0;j<alter;j++)
		{
			int index,nprice;
			cin>>index>>nprice;
			add(index,i,nprice);//反向汇集 
		}
	}
	int res=INF;
	for(int i=status[1]-m;i<=status[1];i++)
		res = min(res,dijkstra(i,i+m));
	cout<<res;
	return 0;
}

单源最短路的综合应用

AcWing 1135. 新年好

重庆城里有 n 个车站,m 条 双向 公路连接其中的某些车站。

每两个车站最多用一条公路连接,从任何一个车站出发都可以经过一条或者多条公路到达其他车站,但不同的路径需要花费的时间可能不同。

在一条路径上花费的时间等于路径上所有公路需要的时间之和。

佳佳的家在车站 1,他有五个亲戚,分别住在车站 a,b,c,d,e。

过年了,他需要从自己的家出发,拜访每个亲戚(顺序任意),给他们送去节日的祝福。

怎样走,才需要最少的时间?

输入格式
第一行:包含两个整数 n,m,分别表示车站数目和公路数目。

第二行:包含五个整数 a,b,c,d,e,分别表示五个亲戚所在车站编号。

以下 m 行,每行三个整数 x,y,t,表示公路连接的两个车站编号和时间。

输出格式
输出仅一行,包含一个整数 T,表示最少的总时间。

数据范围
1≤n≤50000,
1≤m≤105,
1<a,b,c,d,e≤n,
1≤x,y≤n,
1≤t≤100

  • 思路:先确定第一家拜访的人家,再确定第二家......最后确定第五家,要找到最小的距离是从我家到第一家的距离+第一家到第二家的距离+......+第四家到第五家的距离,而这可以通过DFS来搜索一下。同时关于距离的信息,要用dijkstra或者其他最短路算法先预处理一下。(要跑五遍,既要知道A到B、C、D、E、起点,也要知道B到C、D、E、A、起点的最短距离,然后用二维数组存储一下,可能有其他做法)
#include<bits/stdc++.h>
using namespace std;

typedef pair<int ,int > PII;

const int N = 5E4+10,M = 2E5+10,INF = 0x3f3f3f3f;
int pos[6],mapp[6][6];
int dist[N];
bool used[N];

int e[M],ne[M],w[M],h[N],idx=0;
void add(int a,int b,int c)
{
	e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}

void dijkstra(int st)
{
	memset(dist,0x3f,sizeof(dist));
	memset(used,false,sizeof(used));
	priority_queue<PII,vector<PII>,greater<PII> > heap;
	heap.push(PII(0,pos[st]));
	dist[pos[st]] = 0;
	while(heap.size())
	{   
		PII t = heap.top();
		heap.pop();
		
		int d = t.first,u=t.second;
		if(used[u])continue;
		used[u] = true;
		for(int i = h[u];~i;i=ne[i])
		{   
			int j = e[i];
			if(dist[j]>d+w[i])
			{
				dist[j]=d+w[i];
				heap.push(PII(dist[j],j));
			}
		}
	}
	mapp[0][st] = mapp[st][0] = dist[1];
	for(int i=1;i<=5;i++)
	   if(i!=st)mapp[i][st]=mapp[st][i] =dist[pos[i]];
}

int dfs_ans=INF;
void dfs(int last,int t,int cnt)
{
	if(cnt==6)
	{
		dfs_ans=min(dfs_ans,t);
		return ;
	}
	
	for(int i=1;i<=5;i++)
	{
		if(!used[i])
		{
			used[i]=true;
			dfs(i,t+mapp[last][i],cnt+1);
			used[i]=false;	
		}
	}
}

void init()
{
	memset(h,-1,sizeof(h));
}

int main()
{   
    init();
    int n,m;
    cin>>n>>m;
    
    for(int i=1;i<=5;i++)
        cin>>pos[i];
        
    for(int i=1;i<=m;i++)
    {
    	int x,y,t;
    	scanf("%d%d%d",&x,&y,&t);
    	add(x,y,t);add(y,x,t);
	}
	
	for(int i=1;i<=5;i++)
	    dijkstra(i); 
	memset(used,false,sizeof(used));
	dfs(0,0,1);
	cout<<dfs_ans;
	return 0;
}
posted @ 2021-07-27 17:20  BeautifulWater  阅读(162)  评论(0编辑  收藏  举报