zoukankan      html  css  js  c++  java
  • UVA

    /*
      总体思路见小白书 P173 的讲解,不过小白书非常言简意赅,有点不好理解,所以我把自己的一些理解,加到了代码注释里
     
    
      收获:
      1. C++ 中,bool 类型的 true 等价于 int 类型的 1, bool 类型的 false 等价于 int 类型的 0
      参见 ( https://stackoverflow.com/questions/5369770/bool-to-int-conversion )
      
      摘取:
      If the source type is bool, the value false is converted to zero and the value true is converted to one.
      
      说明:
      1. 凡是加上了 (int) 强制类型转换的,基本都是因为,STL中的size()函数,返回的并不一定是int类型,按专业的说法,返回的应该是 size_t类型,所以还是应该转换的,虽然大部分时候,不强制转换好像也不会有错,但是,既然知道了这个知识点,最好还是严谨点
    
    */


    #include <iostream>
    #include <cstring>
    #include <vector>
    #include <queue>
    #define rep(i, n) for ( int i = 0; i < (n); i++ )
    using namespace std;
    
    const int N = 1e5 + 5;
    const int INF = 0x3f3f3f3f;
    
    struct edge
    {
    	int u, v, c;
    	edge ( int u = 0, int v = 0, int c = 0 ) : u(u), v(v), c(c)
    	{
    	}
    };
    
    vector<edge> edges;
    vector<int> G[N]; 
    vector<int> ans;
    int n, vis[N];
    int d[N];
    
    void addEdge (int u, int v, int c)
    {
    	edges.push_back ( edge(u, v, c) );
    	int idx = (int)edges.size() - 1;
    	G[u].push_back(idx); // G[u] 记录了所有与 u 邻接的边,在 edges 中的下标 
    }
    
    // 反向 bfs, 以找到每个结点 i 到终点的最短距离 d[i]
    void rev_bfs()
    {
    	memset( vis, 0, sizeof(vis) );
    	d[n - 1] = 0;
    	vis[n - 1] = true;
    	
    	queue<int> q;
    	q.push(n - 1);
    	while ( !q.empty() ) 
    	{
    		int v = q.front();
    		q.pop();
    		rep( i, (int)G[v].size() )
    		{
    			int e = G[v][i];
    			int u = edges[e].v; 
    			/*
    			u 是所有与 v 相邻的结点,u 结点如果已经被访问过,说明最短距离 d[u] 已被赋值,且肯定比 v 想要赋给 u 的 d[v] + 1 要小 (这个是通过遍历的规律能推出来的,不过我不知道如何严谨证明),所以如果已经被赋值,我们就不去更改了;
    			
    			只对没有赋值的相邻结点作如下处理:更改其d[u],并设置标记 vis[u] (使其的 d[u]不会被 u 的其他相邻结点更改,因为 d[u] 不可能比当前更小了),并把 u 结点的标号压栈
    			*/
    			if ( !vis[u] ) 
    			{
    				vis[u] = true;
    				d[u] = d[v] + 1;
    				q.push(u);
    			}
    		}
    	}
    }
    
    /*
     *从起点开始走,但是每次达到一个新结点时,要保证d值恰好减少 1, 如果有多种走法,选颜色字典序最小的走, 
     *相当于再做了一次 BFS 
    */
    void bfs()
    {
    	memset( vis, 0, sizeof(vis) );
    	vis[0] = true;
    	ans.clear();
    	
    	vector<int> next;
    	next.push_back(0);
    	
    	rep ( i, d[0] ) //要选取的路线,使得 d 值恰好减少1,而起点到终点的最短距离又是 d[0],故而循环次数就为 d[0]
    	{
    		int min_color = INF;
    		rep ( j, (int)next.size() ) // next中存放了上一步的所有可能解 ( 可能有多条边颜色字典序最小,那么走下一步时,要把所有这些可能的解,都当作起点考虑一遍 )
    		{
    			int u = next[j];
    			rep ( k, (int)G[u].size() ) //找到这些可能的“起点”的临边,逐一分析
    			{
    				int e = G[u][k];
    				int v = edges[e].v;
    				if ( d[u] == d[v] + 1 ) //如果有恰好能走一步的情况,则看能否更新当前的 min_color,可以则更新
    				min_color = min ( min_color, edges[e].c );
    			}
    		}
    		ans.push_back(min_color); //这是从所有可能的“起点”开始,考虑了其临边以后,我们得到的,这一步真正该走的颜色,压入该颜色
    		
    		
    		/*
    		因为最初可能有多个字典序最小,在 rep ( i, d[0] ) 循环中的 rep ( j, (int)next.size() )里,其实就是以这些点为起点,考虑其所走的下一步,该取哪个颜色;
    		
    		但是,走完下一步以后,其实还是有可能有多解,下面的这些代码,就是找出再走一步时的多解 (这些解的起点必定来自 next,且它们选取的颜色,肯定和要走的下一步,所选取的 min_color 颜色相等,且在走的过程中,必定也是 d 每次递减 1 的),把这些多解都压入 next2 这个 vector 中,并将其赋给 next 这个 vector,在 i 循环的下一轮循环里,我们就又从 next里的多解来考虑 ( 此时next 已经变成,走下一步时,保存的多解了 ),如此循环往复,每次往ans里面压入所选取的一个颜色,并再次从多解中,挑出真正满足“最小颜色”、“最小字典序”的解,这样不断地进行剔除,知道选出了从起点走向终点的,共 d[0] 步的走法
    		
    		*/
    		vector<int> next2;
    		rep( j, (int)next.size() )
    		{
    			int u = next[j];
    			rep( k, (int)G[u].size() )
    			{
    				int e = G[u][k];
    				int v = edges[e].v;
    				
    				if ( d[u] == d[v] + 1 && !vis[v] && edges[e].c == min_color )
    				{
    					vis[v] = true;
    					next2.push_back(v);
    				}
    			}
    		}
    		next = next2; //话说,这个的思路,好像有点像,DP 里的滚动数组呢!~就是永远是两个容器来保存,进行下一轮时,要以另一个容器的值作为新的启示值
    		//虽然好像和 DP 的滚动数组,还是有不少差别的,但我觉得思想上,可以类比帮助理解,因为这题比滚动数组还抽象,比喻一下以后,感觉更好理解了一些 
    	}
    	cout << (int)ans.size() << endl;
    	cout << ans[0];
    	for ( int i = 1; i < (int)ans.size(); i++) cout << " " << ans[i];
    	cout << endl;
    }
    
    int main ()
    {
    	int u, v, c, m;
    	while (cin >> n >> m)
    	{
    		edges.clear();
    		rep(i, n) G[i].clear();
    		
    		while (m--)
    		{
    			cin >> u >> v >> c;
    			addEdge(u - 1, v - 1, c);
    			addEdge(v - 1, u - 1, c); //注意,该题是无向图问题,所以要将起点终点颠倒,再建一次边
    		}
    		rev_bfs(); // reverse-bfs 反向bfs
    		bfs(); 		
    	}
    	return 0;
    }


  • 相关阅读:
    T4 assembly
    HtmlPrefixScopeExtensions
    PetaPoco修改
    类属性赋值
    Microsoft Office 2007的ContentType
    年月日-正则
    Unity shader学习之逐顶点漫反射光照模型
    Unity3d之表情动画--眨眼
    shader之法线变换
    Unity之fragment shader中如何获得视口空间中的坐标
  • 原文地址:https://www.cnblogs.com/mofushaohua/p/7789388.html
Copyright © 2011-2022 走看看