ACM 预备队训练 第八周 最短路径

一.B3647 【模板】Floyd

思路:虽然这题是Floyd的模版题,但我使用使用范围更广的dijkstra算法(迪杰斯特拉算法)做的。

代码:

//最短路径问题  迪杰斯特拉算法 
//无向图的短路径。
#include<bits/stdc++.h>
using namespace std;
int M = 0x7FFFFFFF;
int n,m,a[101][101],min1,next1=77;
int vis[101],dis[101];
int terminal1[101];
void min_distance (int star)
{
	for (int i=1;i<=n;i++)   //初始化 
    {
    	//terminal1[i];
    	dis[i]=M;
    	vis[i]=0;
    }
	terminal1[star]=0;
	dis[star]=0;
	vis[star]=1;
	while (star <= n)
	{
		min1 = M;
		for (int i=1;i<=n;i++)
		{
			if (a[star][i]!= M && vis[i]==0)
				dis[i]= min(dis[i],a[star][i]+dis[star]);
			if (vis[i]==0 && dis[i] < min1)
			{
				next1=i;
				min1=dis[i];
			}
		}
		//cout<<next<<"  "<<min1<<endl;
		if (min1==M) break;
		terminal1[next1]=min1;
		vis[next1]=1;
		star = next1;
	}
	for (int i=1;i<=n;i++)  cout<<terminal1[i]<<" ";
	cout<<"\n";
	return ;
}
 
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
    cin>>n>>m;
    for (int i=1;i<=n;i++)   //初始化 
    {
    	dis[i]=M;
    	for (int j=1;j<=n;j++)
    		a[i][j]=M;
	}
    while (m--)		 //用邻接矩阵保存图的信息 
    {
    	int u,v,w;
    	cin>>u>>v>>w;
    	a[u][v]=min(a[u][v],w);  //严谨 
    	a[v][u]=a[u][v];   //无向图 	
	}
	for (int i=1;i<=n;i++) min_distance [i];
    return 0;
}

二.P4779 【模板】单源最短路径(标准版)

思路:这题我一开始是用上面一样的方法做的,但本题数据过多,用邻接矩阵来存图可能会超时和超内存以及WA,所以我使用向前星链表来存图,并使用优先队列来替代每次的找与本次start点最近的点,这样可以缩短时间复杂度。

代码:

#include<bits/stdc++.h>
using namespace std;
const int MaxN = 100010, MaxM = 500010;
struct edge
{
    int to, dis, next;
};

edge e[MaxM];
int head[MaxN], dis[MaxN], cnt;
bool vis[MaxN];
int n, m, s;

inline void add_edge( int u, int v, int d )
{
    cnt++;
    e[cnt].dis = d;
    e[cnt].to = v;
    e[cnt].next = head[u];
    head[u] = cnt;
}

struct node
{
    int dis;
    int pos;
    bool operator <( const node &x )const
    {
        return x.dis < dis;
    }
};

std::priority_queue<node> q;


inline void dijkstra()
{
    dis[s] = 0;
    q.push( ( node ){0, s} );
    while( !q.empty() )
    {
        node tmp = q.top();
        q.pop();
        int x = tmp.pos, d = tmp.dis;
        if( vis[x] )
            continue;
        vis[x] = 1;
        for( int i = head[x]; i; i = e[i].next )
        {
            int y = e[i].to;
            if( dis[y] > dis[x] + e[i].dis && !vis[y])
            {
                dis[y] = dis[x] + e[i].dis;
                if( 1 )
                {
                    q.push( ( node ){dis[y], y} );
                }
            }
        }
    }
}


int main()
{
    scanf( "%d%d%d", &n, &m, &s );
    for(int i = 1; i <= n; ++i)dis[i] = 0x7fffffff;
    for( register int i = 0; i < m; ++i )
    {
        register int u, v, d;
        scanf( "%d%d%d", &u, &v, &d );
        add_edge( u, v, d );
    }
    dijkstra();
    for( int i = 1; i <= n; i++ )
        printf( "%d ", dis[i] );
    return 0;
}

三.P2661 [NOIP2015 提高组] 信息传递

思路:并查集求最小环

把每个同学看成一个点,信息的传递就是在他们之间连有向边,游戏轮数就是求最小环。

图论求最小环,我在里面看到了并查集。

假如说信息由A传递给B,那么就连一条由A指向B的边,同时更新A的父节点,A到它的父节点的路径长也就是B到它的父节点的路径长+1。

这样我们就建立好了一个图,之后信息传递的所有环节都按照这些路径。游戏结束的轮数,也就是这个图里最小环的长度。

如果有两个点祖先节点相同,那么就可以构成一个环,长度为两个点到祖先节点长度之和+1。

代码

#include<cstdio>
#include<iostream>
using namespace std;
int f[200002],d[200002],n,minn,last;   //f保存祖先节点,d保存到其祖先节点的路径长。 
int fa(int x)
{
    if (f[x]!=x)                       //查找时沿途更新祖先节点和路径长。 
    {
        int last=f[x];                 //记录父节点(会在递归中被更新)。 
        f[x]=fa(f[x]);                 //更新祖先节点。 
        d[x]+=d[last];                 //更新路径长(原来连在父节点上)。 
    }
    return f[x];
}
void check(int a,int b)
{
    int x=fa(a),y=fa(b);               //查找祖先节点。 
    if (x!=y) {f[x]=y; d[a]=d[b]+1;}   //若不相连,则连接两点,更新父节点和路径长。 
    else minn=min(minn,d[a]+d[b]+1);   //若已连接,则更新最小环长度。 
    return;
}
int main()
{
    int i,t;
    scanf("%d",&n);
    for (i=1;i<=n;i++) f[i]=i;         //祖先节点初始化为自己,路径长为0。 
    minn=0x7777777;
    for (i=1;i<=n;i++)
    {
        scanf("%d",&t);
        check(i,t);                    //检查当前两点是否已有边相连接。 
    }
    printf("%d",minn);
    return 0;
}
 

本题解来自:题解 P2661 【信息传递】 - anyway - 洛谷博客 (luogu.org);感谢提供此简单方便的解法。

四.P1144 最短路计数

思路:与第2题类似。

注意有坑,小心memset()里面不要用0x7fffffff,会爆的,只能用0x3f3f3f3f。原因是无穷的加法。我在这里卡了好久!!!

代码:

#include<bits/stdc++.h>
using namespace std;
#define M  0x7FFFFFFF 
#define mod 100003
const int maxn = 1e6+2;
const int maxm = 2e6+2;
int n,m;
struct edge
{
	int to,w,nex;
} e[maxm] ;
int cnt=0;
int js[maxn],dis[maxn],head[maxn];
struct node
{
	int fro , dis;
	bool operator < (const node &x) const
	{
		return dis > x.dis;
	}
};
priority_queue <node> q;
inline void add_edge( int u, int v, int d )
{
    cnt++;
    e[cnt].w = d;
    e[cnt].to = v;
    e[cnt].nex = head[u];
    head[u] = cnt;
}
int main()
{
    cin>>n>>m;
    for( int i=1; i<=m; i++ )
    {
        register int u, v;
        scanf( "%d%d", &u, &v );
        add_edge( u, v, 1 );
        add_edge( v, u, 1 );
    }
    //cout<<n<<endl; 
    //memset(dis,0x3f,n);
    /*for(int i=1;i<=n;i++)
		cout<<dis[i]<<'\n';   */
    memset(dis,0x3f,sizeof(dis));  
    dis[1]=0;
    js[1]=1; 
	q.push((node){1,0});
    node a;
    while(!q.empty())          //dijkstra经典套路操作。 
    {
        a=q.top(); 
		q.pop();                               
        int nf=a.fro,dd=a.dis;
		//if(dd>=dis[nf]) continue;  
		//if(dd!=dis[nf]) continue;    
		for(int i=head[nf];i;i=e[i].nex)
		{
			int d=e[i].w;
			int nto=e[i].to;
			if(dd+e[i].w==dis[nto]) 	//一边计算一边模。
            	js[nto]=(js[nf]+js[nto])%mod;
			if (dis[nto] > dis[nf]+d)
			{
                dis[nto]=dis[nf]+d;
				js[nto]=js[nf];                      //找到一条更短的路径是,用它的前驱的js换它。 
                q.push((node){nto,dis[nto]});
               // q.push((node){v,dis[v]});
            }          
		}  
    }
for(int i=1;i<=n;i++)
		cout<<js[i]<<'\n';   
return 0;
}
        

五.P8794 [蓝桥杯 2022 国 A] 环境治理

思路:用二分法来求最少要经过多少天后 P 指标可以满足要求。

代码:

#include<bits/stdc++.h>
using namespace std;
int limit[102][102],n,q,d[102][102];
int dijst(int d[][102])
{
	int m=0;
	for (int k=0;k<n;k++)
	{
	int vis[102],dis[102];
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	int c=0,min1,s=k,nex;
	vis[s]=1;
	dis[s]=0;
	while (c<n-1)
	{
		min1=0x3f3f3f3f;
		for (int i=0;i<n;i++)
		{
			if (vis[i]==0)
			dis[i] = min(dis[i],d[s][i]+dis[s]);
			if (vis[i]==0 && dis[i]<min1)
			{
				min1=dis[i];
				nex=i;
			}
		}
		//if (min1==0x3f3f3f3f) break;
			s=nex;
			vis[nex]=1;
			//cout<<min1<<endl;
			//cout<<nex<<endl;
			c++;
			m+=min1;
	}		
	//cout<<m<<endl;
	}
	return m;
}
int calc(int day)          // 计算经过day 天治理后的P指标值
{
    int tmp[102][102];     // 用于治理的辅助数组
	int i,j,k;
	for (i =0; i<n; i++)
		for (j =0; j<n; j++)
			tmp[i][j]=d[i][j];
	for (i =0; i<n; i++)
	{
		int val = day/n+(day%n>=i+1?1:0);
		for (j =0 ; j<n; j++)
		{
		    tmp[i][j]-=val;
		    if (tmp[i][j]<limit[i][j]) tmp[i][j]=limit[i][j];
		    tmp[j][i]-=val;
		    if (tmp[j][i]<limit[j][i]) tmp[j][i]=limit[j][i];
		}	
	}
	return dijst(tmp)<=q;
}
int main()
{
    cin>>n>>q;
    for (int i=0;i<n;i++)
    	for (int j=0;j<n;j++)
    	cin>>d[i][j];
    for (int i=0;i<n;i++)
    	for (int j=0;j<n;j++)
    	cin>>limit[i][j];
	//cout<<dijst(d)<<endl;
	if (dijst(d)<=q) 
	{
		cout<<0;
		return 0;
	}
	// 二分搜索
	int left=1,right=100000*n-1 ,mid,ans;
	
	while (left<=right)
	{
		mid=(left+right)>>1;
		//mid = (left+right)/2;
		if (calc(mid))
		{
			right= mid -1;
			ans = mid;
		}
		else
			left=mid+1;
	}
	if (calc(ans))
	cout<<ans;
	else
	cout<<-1;
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值