zoukankan      html  css  js  c++  java
  • 洛谷 P1629 邮递员送信 题解

    P1629 邮递员送信

    题目描述

    有一个邮递员要送东西,邮局在节点1.他总共要送N-1样东西,其目的地分别是2~N。由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有M条道路,通过每条道路需要一定的时间。这个邮递员每次只能带一样东西。求送完这N-1样东西并且最终回到邮局最少需要多少时间。

    输入格式

    第一行包括两个整数N和M。

    第2到第M+1行,每行三个数字U、V、W,表示从A到B有一条需要W时间的道路。 满足1<=U,V<=N,1<=W<=10000,输入保证任意两点都能互相到达。

    【数据规模】

    对于30%的数据,有1≤N≤200;

    对于100%的数据,有1≤N≤1000,1≤M≤100000。

    输出格式

    输出仅一行,包含一个整数,为最少需要的时间。

    输入输出样例

    输入 #1复制

    5 10
    2 3 5
    1 5 5
    3 5 6
    1 2 8
    1 3 8
    5 3 4
    4 1 8
    4 5 3
    3 5 6
    5 4 2

    输出 #1复制

    83

    【两种方法】

    【SPFA】

    SPFA

    【注意注意】

    这是单向路单向路单向路!!!
    重要的事情说三遍!!!
    (题目中没点出这点但是我的全WA经历让我深刻的认识到了这一点)

    【思路】

    去和来这是两个完全相反的东西
    SPFA跑一个方向是很轻松的
    然后另一个方向就很难办了
    该怎么办呢?
    n遍SPFA?
    不太可能这就是一道黄题
    对了!可以反向建图!
    将方向反过来建出来的图就是完全相反的
    某个点到x的距离恰好就是回家的最短距离
    这样和正向建图一结合
    就能求出去和回的最短路径了
    然后求和就可以了

    【完整代码】

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    using namespace std;
    const int Max = 100005;
    struct node
    {
    	int y;
    	int ne;
    	int z;
    }a1[Max << 1],a2[Max << 1];
    int n,m,x;
    const int M = 1002;
    int head1[M],head2[M];
    int sum = 0;
    void add1(int x,int y,int z)
    {
    	a1[++ sum].y = y;
    	a1[sum].z = z;
    	a1[sum].ne = head1[x];
    	head1[x] = sum;
    }
    void add2(int x,int y,int z)
    {
    	a2[++ sum].y = y;
    	a2[sum].z = z;
    	a2[sum].ne = head2[x];
    	head2[x] = sum;
    }
    
    int d1[M],d2[M];
    bool use[M];
    void SPFA1()
    {
    	memset(use,false,sizeof(use));
    	queue<int>q;
    	for(register int i = 1;i <= n;++ i)
    		d1[i] = 999999;
    	d1[x] = 0;
    	q.push(x);
    	while(!q.empty())
    	{
    		int qwq = q.front();
    		q.pop();use[qwq] = false;
    		for(register int i = head1[qwq];i != 0;i = a1[i].ne)
    		{
    			int awa = a1[i].y;
    			if(d1[awa] > d1[qwq] + a1[i].z)
    			{
    				d1[awa] = d1[qwq] + a1[i].z;
    				if(use[awa] == false)
    				{
    					use[awa] = true;
    					q.push(awa);
    				}
    			}
    		}
    	}
    }
    void SPFA2()
    {
    	memset(use,false,sizeof(use));
    	queue<int>q;
    	for(register int i = 1;i <= n;++ i)
    		d2[i] = 999999;
    	d2[x] = 0;
    	q.push(x);
    	while(!q.empty())
    	{
    		int qwq = q.front();
    		q.pop();use[qwq] = false;
    		for(register int i = head2[qwq];i != 0;i = a2[i].ne)
    		{
    			int awa = a2[i].y;
    			if(d2[awa] > d2[qwq] + a2[i].z)
    			{
    				d2[awa] = d2[qwq] + a2[i].z;
    				if(use[awa] == false)
    				{
    					use[awa] = true;
    					q.push(awa);
    				}
    			}
    		}
    	}
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	x = 1;
    	int xx,yy,zz;
    	for(register int i = 1;i <= m;++ i)
    	{
    		scanf("%d%d%d",&xx,&yy,&zz);
    		add1(xx,yy,zz);
    		add2(yy,xx,zz);
    	}
    	SPFA1();
    	SPFA2();
    	int MM = 0;
    	for(register int i = 1;i <= n;++ i)
    		MM = MM + d1[i] + d2[i];
    	cout << MM << endl;
    	return 0;
    }
    

    【dijkstra】

    dijkstra + 堆优化

    【说在前面的话】

    通过这道题发现我真是个可爱的孩子
    用dijkstra做的时候
    开开心心打一个反向建图
    轻轻松松提交上去
    然后拿了70分……
    堆优化之后的复杂度是O((N+M)logN)完全不会超时
    然后我就开始思考各种可能出现的玄学问题
    最后焦灼了一个小时
    终于发现是数组开小了……
    鬼知道为什么数组开小会超时……

    【核心思路】

    有去有回
    如果只是去的话单源最短路径就可以轻轻松松解决掉
    但是来回的话
    首先想到的是多源最短路径
    但是好像跑不过去
    这就用到了和上面SPFA一样的思路了
    就是反向建图
    这样只需要两遍dijkstra就可以解决掉
    最后再求个和就没有问题了

    【完整代码】

    #include<iostream>
    #include<cstdio>
    #include<queue>
    using namespace std;
    const int Max = 100004;
    const int M = 1003;
    struct point
    {
    	int w;
    	int x;
    	bool operator < (const point & xx)const
    	{
    		return xx.w < w;
    	}
    };
    int n,m;
    struct node
    {
    	int y,ne;
    	int z;
    }a1[Max << 1],a2[Max << 1];
    int sum1 = 0,sum2 = 0;
    int head1[M],head2[M];
    
    inline int read()
    {    
         int x=0; char ch=getchar();
         while (ch<'0' || ch>'9') ch=getchar();
         while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
         return x;
    }   
    
    void add1(int x,int y,int z)
    {
    	a1[++ sum1].y = y;
    	a1[sum1].z = z;
    	a1[sum1].ne = head1[x];
    	head1[x] = sum1;
    }
    void add2(int x,int y,int z)
    {
    	a2[++ sum2].y = y;
    	a2[sum2].z = z;
    	a2[sum2].ne = head2[x];
    	head2[x] = sum2;
    }
    int dis1[M],dis2[M];
    bool use1[M],use2[M];
    priority_queue<point>q;
    void dj1()
    {
    	for(register int i = 2;i <= n;++ i)
    		dis1[i] = 999999;
    	dis1[1] = 0;
    	q.push((point){0,1});
    	while(!q.empty())
    	{
    		point qwq = q.top();
    		q.pop();
    		int x = qwq.x;
    		if(use1[x] == true)
    			continue;
    		use1[x] = true;
    		for(register int i = head1[x];i != 0;i = a1[i].ne)
    		{
    			int awa = a1[i].y;
    			if(dis1[awa] > dis1[x] + a1[i].z)
    			{
    				dis1[awa] = dis1[x] + a1[i].z;
    				if(use1[awa] == false)
    					q.push((point){dis1[awa],awa});
    			}
    		}
    	}
    }
    
    void dj2()
    {
    	for(register int i = 2;i <= n;++ i)
    		dis2[i] = 999999;
    	dis2[1] = 0;
    	q.push((point){0,1});
    	while(!q.empty())
    	{
    		point qwq = q.top();
    		q.pop();
    		int x = qwq.x;
    		if(use2[x] == true)
    			continue;
    		use2[x] = true;
    		for(register int i = head2[x];i != 0;i = a2[i].ne)
    		{
    			int awa = a2[i].y;
    			if(dis2[awa] > dis2[x] + a2[i].z)
    			{
    				dis2[awa] = dis2[x] + a2[i].z;
    				if(use2[awa] == false)
    					q.push((point){dis2[awa],awa});
    			}
    		}
    	}
    }
    
    int main()
    {
    	n = read();m = read(); 
    	int x,y,z;
    	for(register int i = 1;i <= m;++ i)
    	{
    		x = read();y = read();z = read();
    		add1(x,y,z);add2(y,x,z);
    	}
    	dj1();
    	dj2();
    	int ans = 0;
    	for(register int i = 2;i <= n;++ i)
    		ans += dis1[i] + dis2[i];
    	cout << ans << endl;
    	return 0;
    }
    
  • 相关阅读:
    oracle中查询或插入特殊字符
    html最多显示两行,css 实现两行或多行文本溢出显示省略号(...)
    RT
    发表一个自己做的WPF游戏
    用silverlight制作自己的GIS
    一个字符串切割问题
    Oracle所有者权限与调用者权限(转)
    Oracle角色权限的使用事项(转)
    Tomcat 内存溢出 详解
    forms验证:怎么验证两种身份?
  • 原文地址:https://www.cnblogs.com/acioi/p/11693050.html
Copyright © 2011-2022 走看看