Loading

Dijkstra(刷题——最短路)

飞行路线

\(n\) 个点 \(m\) 条边的无向连通图,最多可以将 \(k\) 条边的权值赋为 \(0\).求 \(S\)\(t\) 的最短路径

solution

分层图跑最短路,图与图之间的用权值为 \(0\) 的边连接,每下一层代表免费一次,只需求 \(S\)\(t+k*n\)的最短路即可

#include<cstdio>
#include<cctype>
#include<cstring>
#include<queue>
#include<algorithm>
#include<vector>
#include<utility> 
#include<functional>

int Read()
{
    int x=0;char c=getchar();
    while(!isdigit(c))
    {
        c=getchar();
    }
    while(isdigit(c))
    {
        x=x*10+(c^48);
        c=getchar();
    }
    return x;
}

using std::priority_queue;
using std::pair;
using std::vector;
using std::make_pair;
using std::greater;

struct Edge
{
    int to,next,cost;
}edge[2500001];
int cnt,head[110005];

void add_edge(int u,int v,int c=0)
{
    edge[++cnt]=(Edge){v,head[u],c};
    head[u]=cnt;
}

int dis[110005];
bool vis[110005];
void Dijkstra(int s)
{
    memset(dis,0x3f,sizeof(dis));
    dis[s]=0;
    priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > points;
    points.push(make_pair(0,s));
    while(!points.empty())
    {
        int u=points.top().second;
        points.pop();
        if(!vis[u])
        {
            vis[u]=1;
            for(int i=head[u];i;i=edge[i].next)
            {
                int to=edge[i].to;
                if(dis[to]>dis[u]+edge[i].cost) 
                {
                    dis[to]=dis[u]+edge[i].cost;
                    points.push(make_pair(dis[to],to));
                }
            }
        }
    }
}

int main()
{
    int n=Read(),m=Read(),k=Read(),s=Read(),t=Read();
    int u,v,c;
    for(int i=0;i<m;++i)
    {
        u=Read(),v=Read(),c=Read();
        add_edge(u,v,c);
        add_edge(v,u,c);
        for(int j=1;j<=k;++j)
        {
            add_edge(u+(j-1)*n,v+j*n);
            add_edge(v+(j-1)*n,u+j*n);
            add_edge(u+j*n,v+j*n,c);
            add_edge(v+j*n,u+j*n,c);
        }
    }
  //坑人的数据===================================
    for(int i=1;i<=k;++i){
		add_edge(t+(i-1)*n,t+i*n);
	}
  //===========================================
    Dijkstra(s);
    printf("%d",dis[t+k*n]);
    return 0;
}

Telephone Lines S

在加权无向图上求出一条从 \(1\) 号结点到 \(N\) 号结点的路径,使路径上第 \(K+1\) 大的边权尽量小。

solution

二分求最短路,枚举第 \(K+1\) 大的边的长度,如果比枚举的值大,边权就为 \(1\),否则为 \(0\) ,然后跑最短路,如果最短路径大于 \(k\) ,那就合法,继续缩小范围,否则扩大范围

/*
work by:Ariel_
*/
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
#include <utility>
const int M = 2e3 + 5;
int read(){
   int x = 0,f = 1;char c = getchar();
   while(c < '0'||c > '9'){if(c == '-')f = -1;c = getchar();}
   while(c >= '0'&&c <= '9'){x = x*10 + c - '0';c = getchar();}
   return f*x;
}

using std::vector;
using std::pair;
using std::make_pair;
using std::priority_queue;
using std::greater;

int n, p, k;

struct edge{
	int v,nxt,w;
}e[M << 1];
int cnt,head[M];

void add_edge(int u,int v,int w){
   e[++cnt] = (edge){v, head[u], w};
   head[u] = cnt;
}
int dis[M];
bool vis[M];
bool dij(int s,int mid){
   memset(dis,0x3f,sizeof(dis));
   memset(vis,0,sizeof(vis));
   dis[1] = 0;
   priority_queue<pair<int,int> ,vector<pair<int, int> >,greater<pair<int ,int> > >q;
   q.push(make_pair(0, s));
   while(!q.empty()){
   	  int u = q.top().second; q.pop();
   	  if(!vis[u]){
   	  	vis[u] = 1;
   	  	for(int i = head[u]; i;i = e[i].nxt){
   	  	      int v = e[i].v;
		      if(dis[v] > dis[u] + (e[i].w > mid)){
		      	 dis[v] = dis[u] + (e[i].w > mid);
		      	 q.push(make_pair(dis[v], v));
			  }
		  }
	  }
   }
   if(dis[n] <= k) return true;
   else return false ; 
}
int main(){
   n = read(),p = read(),k = read();
   for(int i = 1, u, v, w;i <= p; i++){
   	 u = read(),v = read(),w = read();
     add_edge(u, v, w),add_edge(v, u, w);
   }
   
   int l = 0,r = 1000001,ans = 2100000000;
   while(l <= r){
   	 int mid = (l + r) >> 1;
	   if (dij(1, mid)){
		ans = mid;
		r = mid - 1;
	  }
 	 else
       l = mid + 1;
   }
   if(ans == 2100000000)printf("-1");
   else printf("%d",ans);
}

Cow Party S

\(n\) 个点 \(m\) 条边的有向图,求\(max(min(i~ -> ~x) + min(x ~->~ i))\);

solution

正反图两边最短路

/*
work by:Ariel_
*/
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <utility>
#include <queue>
const int M = 1e5 + 5;
const int N = 1e3 + 2;
int read(){
   int x = 0,f = 1;char c = getchar();
   while(c < '0'||c > '9'){if(c == '-')f = -1;c = getchar();}
   while(c >= '0'&&c <= '9'){x = x*10 + c - '0';c = getchar();}
   return f*x;
}
using std::vector;
using std::priority_queue;
using std::make_pair;
using std::pair;
using std::greater;
using std::max;	
	
struct edge{ 
  int v,nxt,w;
}e[M << 1];
int cnt,head[N];
void add_edge(int u,int v,int w){
	e[++cnt] = (edge){v,head[u], w};
	head[u] = cnt;
}

int dis[N],n, m, x;
bool vis[N]; 
void dij(int s){
   memset(vis,0,sizeof(vis));
   memset(dis,0x3f,sizeof(dis));
   dis[s] = 0;
   priority_queue<pair<int ,int>,vector<pair<int , int> >,greater<pair<int ,int > > >q;
   q.push(make_pair(0, s));
   while(!q.empty()){
   	 int u = q.top().second;
   	 q.pop();
   	 if(!vis[u]){
   	 	vis[u] = 1;
   	    for(int i = head[u]; i;i = e[i].nxt){
   	    	int v = e[i].v;
			if(dis[v] > dis[u] + e[i].w){
			   dis[v] = dis[u] + e[i].w;
			   q.push(make_pair(dis[v],v));
			} 
		 }	 
	 }
   }
}
int main(){
  n = read(),m = read(),x = read();
  for(int i = 1,u, v, w;i <= m; i++){
  	 u = read(),v = read(),w = read();
  	 add_edge(u, v, w);  
  }

  int tot[N];
  for(int i = 1;i <= n; i++){
  	  dij(i);
  	  tot[i] = dis[x];
  }  
  dij(x);
  int ans = 0;
  for(int i = 1;i <= n; i++){
  	 ans = max(ans,tot[i] + dis[i]);
  }
  printf("%d",ans);
}

[USACO06NOV]Roadblocks G

给一张无向图,求这张图的严格次短路之长。

solution

两个 \(dis\) 数组一个存最小值,一个存次小值,维护次小值比最小值大且比其他的边的值小

/*
work by:Ariel_
*/
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#include <utility>
#include <cstring>
const int M = 1e5 + 5;
const int N = 5e3 + 4;
int read(){
   int x = 0,f = 1;char c = getchar();
   while(c < '0'||c > '9'){if(c == '-')f = -1;c = getchar();}
   while(c >= '0'&&c <= '9'){x = x*10 + c - '0';c = getchar();}
   return f*x;
}
using std::queue;

int n, m;
struct edge{
   int v,nxt,w;
}e[M << 1];

int cnt,head[M];
void add_edge(int u,int v,int w){
   e[++cnt] = (edge){v, head[u], w};
   head[u] = cnt;
}
int dis1[N],opt[N],dis2[N];
bool vis[N];
void spfa(int s){
	memset(dis1,0x3f,sizeof(dis1));
	memset(dis2,0x3f,sizeof(dis2));
	dis1[s] = 0;
       queue <int> q;
       q.push(s);
	while(!q.empty()){
	int u = q.front();
    q.pop();vis[u] = 0;
	   for(int i = head[u]; i;i = e[i].nxt){
			int v = e[i].v;
			if(dis1[v] > dis1[u] + e[i].w){
				dis2[v] = dis1[v];
				dis1[v] = dis1[u]+e[i].w;
				if(!vis[v]) q.push(v),vis[v] = 1;
			}
			else if(dis1[u] + e[i].w < dis2[v] && dis1[u] + e[i].w > dis1[v]){
				dis2[v] = dis1[u] + e[i].w;
				if(!vis[v]) q.push(v),vis[v] = 1;
			}
			if(dis2[u] + e[i].w < dis2[v])
			{
				dis2[v] = dis2[u] + e[i].w;
				if(!vis[v]) q.push(v),vis[v] = 1;
			}
       }
	}
} 
int main(){
  n = read(),m = read();
  for(int i = 1,u,v,w;i <= m; i++){
  	  u = read(),v = read(),w = read();
  	  add_edge(u, v, w),add_edge(v, u, w);
  }
  spfa(1);
  printf("%d", dis2[n]);
}

最短路计数

给出一个 \(N\) 个顶点 \(M\) 条边的无向无权图,顶点编号为 \(1 - N\) 问从顶点 \(1\) 开始,到其他每个点的最短路有几条

solution

跑最短路的时候顺便计数,如果\(dis[v] > dis[u] + 1\) 就重新更新 \(ans[v]\) 的值用 \(ans[u]\) 覆盖

如果\(dis[v] == dis[u] + 1\) 就直接 \(ans[v] += dis[u]\) 就好了

/*
work by:Ariel_
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
const int mod = 100003;
const int N = 1e6 + 5;
const int M = 2e6 + 5;
int read(){
   int x = 0,f = 1;char c = getchar();
   while(c < '0'||c > '9'){if(c == '-')f = -1;c = getchar();}
   while(c >= '0'&&c <= '9'){x = x*10 + c - '0';c = getchar();}
   return f*x;
}
using std::queue;
struct edge{
   int v,nxt;
}e[M << 1];
int cnt,head[N];

void add_edge(int u,int v){
    e[++cnt] = (edge){v,head[u]};
    head[u] = cnt;
}
int dis[N],ans[N];
bool vis[N];
void spfa(int x){
	memset(dis, 0x3f,sizeof(dis));
	queue <int> q;
	q.push(x);
	dis[1] = 0;
	vis[1] = 1;
	ans[1] = 1;
	while(!q.empty()){
	   int u = q.front();
	   q.pop();vis[u] = 0;
	   for(int i = head[u]; i;i = e[i].nxt){
	   	    int v = e[i].v;
		     if(dis[v] > dis[u] + 1){
		     	dis[v] = dis[u] + 1;
		     	ans[v] = ans[u];
		     	if(!vis[v]){
		     		vis[v] = 1;q.push(v);
				 }
			 }
			 else if(dis[v] == dis[u] + 1){
			 	 ans[v] += ans[u];
				 ans[v] %= mod;
			 }  
	   }
	}
}
int n,m;

int main(){
   n = read(),m = read();
   for(int i = 1,u, v;i <= m; i++){
   	  u = read(),v = read();
   	  add_edge(u,v);add_edge(v,u);
   }
   spfa(1);
   for(int i = 1;i <= n; i++){
   	 printf("%d\n",ans[i]);
   }
}

新年好

无向图,求 \(1\)号点到其余五个点路径和的最小值

solution

很简单,无向图,分别跑出每个点的最短路,全排列暴力即可

因为 \(0x3f\) 卡了半上午……

/*
work by:Ariel_
*/
#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <vector>
const int N = 5e4 + 5;
const int M = 1e5 + 5;
int read(){
   int x = 0,f = 1;char c = getchar();
   while(c < '0'||c > '9'){if(c == '-')f = -1;c = getchar();}
   while(c >= '0'&&c <= '9'){x = x*10 + c - '0';c = getchar();}
   return f*x;
}
using std::vector;
using std::pair;
using std::make_pair;
using std::greater;
using std::priority_queue;
using std::min;
using std::cout;
int a[7], n, m;
struct edge{
	int v,nxt,w;
}e[M << 1];
int head[N],cnt;
void add_edge(int u,int v,int w){
	e[++cnt] = (edge){v, head[u], w};
	head[u] = cnt;
}
int dis[N][6];
bool vis[N];
void dij(int x,int num){
	memset(vis , 0, sizeof(vis));
	for(int i = 1;i <= n; i++) dis[i][num] = 0x3f3f3f3f;
	priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
	dis[x][num] = 0;
    q.push(make_pair(0, x));
    while(!q.empty()){
       int u = q.top().second;
       q.pop();
       if(vis[u])continue;
       vis[u] = 1;
       for(int i = head[u]; i; i = e[i].nxt){
       	    int v = e[i].v;
       	   	 if(dis[v][num] > dis[u][num] + e[i].w){
       	   	   	dis[v][num] = dis[u][num] + e[i].w;
       	   	    q.push(make_pair(dis[v][num], v));
 			}
	   }
	}
}
int js,bok[7], ans = 0x3f3f3f3f;
void dfs(int x,int tot,int last){
   if(x == 5){
   	  ans = min(ans, tot);
	  return ;
   }
   for(int i = 1;i <= 5; i++){
   	     if(bok[i]) continue;
   	   	   bok[i] = 1;
   	   	   dfs(x + 1,tot + dis[a[i]][last + 1], i);
   	   	   bok[i] = 0;
   }
}
int main(){
   n = read(),m = read();
   for(int i = 1;i <= 5; i++) a[i] = read();
   
   for(int i = 1,u, v, w;i <= m; i++){
   	 u = read(),v = read(),w = read();
   	 add_edge(u, v, w),add_edge(v, u, w);
   }
   
   dij(1, 1);
   for(int i = 1;i <= 5; i++) dij(a[i], i + 1);
   dfs(0, 0, 0);
   printf("%d\n",ans);
}

最优贸易

求一条点和边都可重复的路径,使得路径上存在两点 \(a, b\) ,使得 \(a\) 先比 \(b\) 访问且 \(Vb-Va\) 尽可能地大

solution

分层图最短路,建三层图,在每层图上跑代表没有交易,如果从一层图上了第二层图代表买入,从第二层上到第三层代表卖出,一二层图之间用两点间权值连接,二三层图用负权值连接,跑出最短路,贸易总额就是最短路的相反数

/*
work by:Ariel_
*/
#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
const int N = 1e5 + 5;
const int M = 5e5 + 5; 
int read(){
   int x = 0,f = 1;char c = getchar();
   while(c < '0'||c > '9'){if(c == '-')f = -1;c = getchar();}
   while(c >= '0'&&c <= '9'){x = x*10 + c - '0';c = getchar();}
   return f*x;
}
using std::priority_queue;
using std::vector;
using std::pair;
using std::make_pair;
using std::greater;
using std::max;
struct edge{ 
   int v,nxt,w;
}e[M * 3];
int cnt,head[N * 3];
void add_edge(int u,int v,int w){
	e[++cnt] = (edge){v,head[u], w};
	head[u] = cnt;
} 
bool vis[M * 3];
int dis[M * 3];
void dij(int x){
   memset(dis, 0x3f3f3f, sizeof(dis));
   priority_queue<pair<int ,int>,vector<pair<int , int> >,greater<pair<int ,int> > > q;
   q.push(make_pair(0, x));
   dis[x] = 0;
   
   while(!q.empty()){
   	  int u = q.top().second;q.pop();
   	  for(int i = head[u];i;i = e[i].nxt){
   	  	    int v = e[i].v;
   	  	    if(dis[v] > dis[u] + e[i].w){
   	  	       dis[v] = dis[u] + e[i].w;
			   q.push(make_pair(dis[v],v));	 	
			 } 
		}
   }
}
int n,m,a[N];
int main(){
   n = read(),m = read();
   for(int i = 1;i <= n; i++) a[i] = read();
   for(int i = 1,u, v,flag;i <= m; i++){
   	   u = read(),v = read(),flag = read();
       if(flag == 1){
       	  for(int k = 0;k < 3; k++)
       	  add_edge(u + k * n , v + k * n, 0);
       	  
       	  add_edge(u, v + n, a[v]);
		  add_edge(u + n,v + n + n, -a[v]);//卖出去 
	   }
	  else{
	   	   for(int k = 0;k < 3; k++){
	   	   	 add_edge(u + k * n,v + k * n, 0);
	   	   	 add_edge(v + k * n,u + k * n, 0);
		   }
		  add_edge(u, v + n, a[v]);
		  add_edge(v, u + n, a[u]);
       	  add_edge(u + n,v + n * 2, -a[v]);
       	  add_edge(v + n,u + n * 2, -a[u]);
	   }
	   add_edge(n, 3 * n, 0);
   }
   dij(1);
   int ans = 0;
   ans = max(ans,-dis[n*3]);
   printf("%d",ans); 
}

汽车加油行驶问题

给你一个\(N*N\)的矩阵,求汽车从\((1,1)\)\((N,N)\)的最小花费,汽车行进一格需要消耗一点油,汽车油储量为 \(k\).

1.若汽车到达一个有加油站的点,那么必须将油加满为 \(k\),花费为 \(a\);

2.若汽车往回走,即前往的点的横坐标或者纵坐标在减小,需要花费 \(b\);

3.汽车也可以自己新建一个加油站,花费\(c\) (不包括加油费用 \(a\) )

solution

分层图,最短路;\(k\) 的范围很小,我们可以建 \(k + 1\) 层图跑最短路,具体就是以上三种情况

注意特殊情况:恰好到终点,恰好没有,此时不需要加油,所以需要打个特判

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
const int N = 101;
const int dx[4] = {0, 1, 0, -1};
const int dy[4] = {1, 0, -1, 0};
int read(){
   int x = 0,f = 1;char c = getchar();
   while(c < '0'||c > '9'){if(c == '-')f = -1;c = getchar();}
   while(c >= '0'&&c <= '9'){x = x*10 + c - '0';c = getchar();}
   return f*x;
}
int n,k,a,b,c;
int mp[N][N],dis[N][N][11];
bool vis[N][N][11];
priority_queue<pair<int,pair<int,pair<int ,int> > > >q;
void dij(){
	memset(dis, 0x3f3f3f3f, sizeof(dis));
	dis[1][1][k] = 0;
	q.push(make_pair(0,make_pair(k,make_pair(1, 1))));
	while(!q.empty()){
	  int z = q.top().second.first,x = q.top().second.second.first,y = q.top().second.second.second;q.pop();//x,y为坐标,z为油量
		if(!vis[x][y][z]){
			vis[x][y][z] = 1;
			for(int i = 0;i < 4; i++){
				int nx =x + dx[i],ny = y + dy[i],nz = z - 1,cost = dis[x][y][z];
	   	  	     if(nx > 0&&nx <= n&&ny > 0&&ny <= n){
					if(mp[nx][ny] == 1){//强制消费
						cost += a;nz = k;
					}
				  if(!nz)//没有油了也没遇见加油站就建一个 
				    cost += c + a,nz = k;
				  if(i >= 2)//向后走
				    cost += b;
				   if(cost < dis[nx][ny][nz]){
				   	  dis[nx][ny][nz] = cost;
				   	  q.push(make_pair(-cost,make_pair(nz,make_pair(nx, ny))));
				   } 
			   }
			}
	   }
    }
    
    int ans = 0x3f3f3f3f;
    if(dis[n][n][k] != 0x3f3f3f3f) dis[n][n][0] = dis[n][n][k] - a - c;//走到终点恰好没有,特判 
    for(int i = 0;i < k;i++){
    	ans = min(ans,dis[n][n][i]);
	}
	printf("%d",ans);
}
int main(){
	n = read(),k = read(),a = read(),b = read(),c = read();
	for(int i = 1;i <= n; i++){
	  for(int j = 1;j <= n; j++){
	  	  mp[i][j] = read();
	  }
	}
	dij();
}
posted @ 2021-01-31 20:31  Dita  阅读(80)  评论(0编辑  收藏  举报