zoukankan      html  css  js  c++  java
  • P1186 玛丽卡

    题目非常得不简洁,所以这里还是做一下翻译吧

    总的来说,就是给你一个图,有一些无向边,然后有一条边可能会堵车(可以理解为这条路不能走),这条边是任意的。求一个时间,这个时间满足在任意一条路堵车的时候,玛丽卡都可以从起点走到终点

    那其实不难发现,这道题的答案就是在少一条边的情况下,求出所有最短路中最长的一条,这其实很好实现,枚举每一条边,使这一条边拥堵,其他边仍然联通,跑最短路,存储答案后再从所有最短路中选一条最大的就行了。到此就可以写出第一个程序(emmm..我这个FW连这个暴力都只有30,他们都有50...)

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN=1e7+50;
    const int INF=20040915;
    int n,m;
    struct node{
    	int x,y,z;
    }a[MAXN];
    int head[MAXN],tot;
    struct edge{
    	int net,to,w;
    }e[MAXN];
    void add(int u,int v,int w){
    	e[++tot].net=head[u];
    	e[tot].to=v;
    	e[tot].w=w;
    	head[u]=tot;
    }
    bool v[MAXN];
    int d[MAXN],sum,ans[MAXN];
    void dij(int s){
    	priority_queue<pair<int,int> > q;
    	for(register int i=1;i<=n;i++){
    		d[i]=INF;
    		v[i]=false;
    	} 
    	d[s]=0;
    	q.push(make_pair(0,s));
    	while(!q.empty()){
    		int x=q.top().second;
    		q.pop();
    		if(v[x]==true) continue;
    		v[x]=true;
    		for(register int i=head[x];i;i=e[i].net){
    			int y=e[i].to,z=e[i].w;
    			if(d[y]>d[x]+z){
    				d[y]=d[x]+z;
    				q.push(make_pair(-d[y],y));
    			}
    		}
    	}
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(register int i=1;i<=m;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
    	for(register int i=1;i<=m;i++){ //枚举任意一条边拥堵的情况 
    		memset(e,0,sizeof e); //记得清零 
    		for(register int j=1;j<=m;j++){ //让其他边正常连通 
    			if(i!=j){
    				add(a[j].x,a[j].y,a[j].z);
    				add(a[j].y,a[j].x,a[j].z); //双向边 
    			}
    		}
    		dij(1);
    		ans[++sum]=d[n]; //记录答案 
    	}
    	int res=-INF;
    	for(register int i=1;i<=sum;i++){
    		res=max(res,ans[i]);
    	}
    	cout<<res;
    	return 0;
    }
    

    好了,意料之中的超时了。如果要优化,肯定是因为有些边没啥用,所以我们看一看样例的情况,如图(粉色的表示在所有边连通时的最短路),然后我们再来手模一下上面的程序:

    1 -> 2 (8) : 21 //改变 
    1 -> 4 (10) : 9
    2 -> 4 (10) : 9
    2 -> 5 (1) : 27 //改变 
    2 -> 3 (9) : 9
    3 -> 5 (10) : 9
    3 -> 4 (7) : 9
    
    //表示在该边拥堵之后的最短路 
    

    这么看来,答案就很显然了。当我们在改变原最短路上的任意边之后,最短路就会改变,这是很显然的,其他边的拥堵完全不会影响到原最短路。我们就可以先跑一遍最短路记录路径,然后让路径拥堵,再跑最短路就可以了

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN=1e7+50;  //边要开两倍数组,在保证不会超出限制的情况下,多开也没什么吧 
    int n,m;
    struct node {
    	int net,to,w,from;
    } e[MAXN];
    int head[MAXN],tot;
    int d[MAXN],v[MAXN];
    int bian[MAXN];//记录边 
    void add(int u,int v,int w) {
    	e[++tot].net=head[u];
    	e[tot].to=v;
    	e[tot].from=u;//这个用来表示前驱节点 
    	e[tot].w=w;
    	head[u]=tot;
    }//链式前向星建边 
    void dij(int s,int p) {//第一个参数表示起点,第二个参数表示哪一条边拥堵 
    	priority_queue<pair<int,int> >q;
    	for(register int i=1; i<=n; i++) d[i]=20040915,v[i]=false; //初始化,memset谨慎使用 
    	d[s]=0;
    	q.push(make_pair(0,s));
    	while(!q.empty()) {
    		int x=q.top().second;
    		q.pop();
    		if(v[x]==true) continue;
    		v[x]=true;
    		for(register int i=head[x]; i; i=e[i].net) {
    			if(i!=p) { //如果该边没有拥堵 
    				int y=e[i].to,z=e[i].w;
    				if(d[y]>d[x]+z) {
    					if(p==-1) bian[y]=i; //第一次跑最短路的时候用来记录路径 
    					d[y]=d[x]+z;
    					q.push(make_pair(-d[y],y));
    				}
    			}
    
    		}
    	}
    }
    int Max(int x,int y){
    	if(x>=y) return x;
    	return y;
    }//手打max有些时候会快些(亲身经历) 
    int main() {
    	scanf("%d%d",&n,&m);
    	for(register int i=1; i<=m; i++) {
    		int u,v,w;
    		scanf("%d%d%d",&u,&v,&w);
    		add(u,v,w);
    		add(v,u,w);
    	}//输入,建双向边 
    	dij(1,-1);//第一次跑最短路没有边拥堵 
    	int ans=d[n],t=n; 
    	while(t){ //枚举最短路上的每一条边,然后更新答案 
    		dij(1,bian[t]); 
    		ans=Max(ans,d[n]);
    		t=e[bian[t]].from;
    	}
    	printf("%d",ans);
    	return 0;
    }
    
  • 相关阅读:
    IO流中文件和文件夹的删除程序举例
    2.14 小结
    mysql数据库分库分表(Sharding)
    django 连接mysql
    网页页面自动刷新代码
    python you-get 下载视频
    爬虫米扑代理
    python urlretrieve 下载图片
    UnicodeDecodeError: 'gbk' codec can't decode byte 0xae in position 120: illegal multibyte sequence
    create a bootable USB stick on Ubuntu
  • 原文地址:https://www.cnblogs.com/Poetic-Rain/p/13207864.html
Copyright © 2011-2022 走看看