NEUQ-ACM Week8

B3647 【模板】Floyd

思路:

1.输入两个方向的线条权值(取最小值)

2.若i-i则一定为0

3.依次循环遍历找最短距离:i到j的最短距离不外乎存在经过i与j之间经过k和不经过k两种可能,所以可以令k=1,2,3,...,n,在检查d(ij)与d(ik)+d(kj)的值;在此d(ik)与d(kj)分别是目前为止所知道的i到k与k到j的最短距离,因此d(ik)+d(kj)就是i到j经过k的最短距离。所以,若d(ij)>d(ik)+d(kj),就表示从i出发经过k再到j的距离要比原来的i到j距离短,自然把i到j的d(ij)重写为d(ik)+d(kj),每当一个k查完了,d(ij)就是目前的i到j的最短距离。重复这一过程,最后当查完所有的k时,d(ij)里面存放的就是i到j之间的最短距离了。

代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,u,v,w,d[105][105];
int main(){
	cin>>n>>m;
	memset(d,0x3f,sizeof(d));
	for(int i=1;i<=n;i++)
		d[i][i]=0;
	for(int i=1;i<=m;i++){
		cin>>u>>v>>w;
		d[u][v]=d[v][u]=min(d[u][v],w);
	}
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cout<<d[i][j]<<" ";
		}
		cout<<endl;
	}
}

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

思路:

1.dijsktra的算法:先循环每个点先找出最小的点再把与找出的这个点相连的点的最短路进行更新,判断冗余,会超时需要优化

2.用priority_queue来实现堆优化dijsktra算法:,在一开始我们直接取出队头元素,判断这个元素是否已经在之前找到过标记过,如果没有则根据邻接表直接更新与其相连的点的最短路的值,并且把与其相连的点全部都放入队列中,这样就可以直接做到朴素算法中原本循环每个点进行找当前最小dis值的过程了。

代码:
#include<bits/stdc++.h>
using namespace std;
int b[200001],n,m,s,top,dis[200001],h[200001];
struct CZP{
	int next,to,di;
}a[200001]; 
struct node{
	int dis;
	int pos;
	bool operator <(const node &x)const{
		return x.dis<dis;
	}
};
void cun(int from,int to,int dis){
	a[++top].next=h[from];
	a[top].to=to;
	a[top].di=dis;
	h[from]=top;
}
priority_queue<node>q;
int main(){
	cin>>n>>m>>s;
	for(int i=1;i<=n;i++)
	    h[i]=-1;
	for(int i=1;i<=m;i++){
		int x,y,z;
		cin>>x>>y>>z;
		cun(x,y,z);
	}
	for(int i=1;i<=n;i++)
	    dis[i]=1e9;
	dis[s]=0;
	q.push((node){0,s});
	while(!q.empty()){
		node x=q.top();
		q.pop();
		int k=x.pos;
		if(b[k]==1) continue;
		b[k]=1;
        int v=h[k];
		while(v!=-1){
			if(dis[a[v].to]>dis[k]+a[v].di){
				dis[a[v].to]=dis[k]+a[v].di;
				if(!b[a[v].to])
				q.push((node){dis[a[v].to],a[v].to});
			}
			v=a[v].next;
		}
	}
	for(int i=1;i<=n;i++) 
	    cout<<dis[i]<<" ";
	return 0;
}

P2661 [NOIP2015 提高组] 信息传递

思路:

1.每次信息传递即一条有向线段连两个人,若有人知道所有人的生日即图中形成了一个环,因此题目即求最小环数

2.在连接一个点到另一个点之前,先用并查集判断是否构成一个环,如果是的话,我们就可以记录下这个环数(cnt),然后比较得出最小的环数

代码:
#include<bits/stdc++.h>
using namespace std;
int n,fa[200005],s=0x3f3f3f3f;
int find(int x,int &cnt){
    cnt++;
    if(fa[x]==x) 
	    return x;
    else 
	    return find(fa[x],cnt);
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)
        fa[i]=i;
    for(int i=1;i<=n;i++){
        int cnt=0,f;
        cin>>f;
        if(find(f,cnt)==i)
            s=min(s,cnt);
		else
        	fa[i]=f;
    }
    cout<<s;
    return 0;
}

P1144 最短路计数

思路:

1.(无向无权) bfs搜索最短路径

代码:
#include<bits/stdc++.h>
using namespace std;
vector<int>a[1000005];
int b[1000005],c[1000005],f[1000005];
int main(){
    int n,m;
	cin>>n>>m;
    for(int i=1;i<=m;i++){
        int x,y;
		cin>>x>>y;
        a[x].push_back(y);
        a[y].push_back(x);
    }
    queue<int>q;
	b[1]=0;
	f[1]=c[1]=1;
	q.push(1);
    while(!q.empty()){
        int x=q.front();
		q.pop();
        for(int i=0;i<a[x].size();i++){
            int t=a[x][i];
            if(f[t]!=1){
			    f[t]=1;
				b[t]=b[x]+1;
				q.push(t);
			}
            if(b[t]==b[x]+1)
			    c[t]=(c[t]+c[x])%100003;
        }
    }
    for(int i=1;i<=n;i++){
        cout<<c[i]<<endl;
    }
    return 0;
}

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

思路:

1.只可能让两点之间灰尘度的值变小,具有单调性,所以可以二分查找可达标天数mid/day答案。

2.计算经过 mid天治理后的 P 指标值。若 P 指标值满足要求,说明还可以减少治理天数;反之增加。

3.同时可以使用Floyd计算任意两个城市间的最短距离

代码:
#include<bits/stdc++.h>
using namespace std;
int d[105][105],limit[105][105],n;
int f(int day){         
    int tmp[105][105];   
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			tmp[i][j]=d[i][j];
	for(int i=0;i<n;i++){
		int val=day/n+(day%n>=i+1?1:0);
		for(int 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];
		}
	}
	for(int k=0;k<n;k++)     
		for(int i=0;i<n;i++)
			for(int j=0;j<n;j++)
				tmp[i][j]=min(tmp[i][j],tmp[i][k]+tmp[k][j]);
	int res=0;
	for(int i=0;i<n;i++)    
		for(int j=0;j<n;j++)
			res+=tmp[i][j];
	return res;
}
int main(){
    int q;
	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];
	int left=0,right=100000*n,ans=-1;
	while(left<=right){
		int mid=(left+right)/2;
		if(f(mid)<=q){
			right=mid -1;
			ans=mid;
		}
		else
			left=mid+1;
	}
	cout<<ans<<endl;
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值