zoukankan      html  css  js  c++  java
  • AOJ 2200 Mr. Rito Post Office (floyd+DP)

    题意:

    快递到了:你是某个岛国(ACM-ICPC Japan)上的一个苦逼程序员,你有一个当邮递员的好基友利腾桑遇到麻烦了:全岛有一些镇子通过水路和旱路相连,走水路必须要用船,在X处下船了船就停在X处。而且岛上只有一条船,下次想走水路还是得回到X处才行;两个镇子之间可能有两条以上的水路或旱路;邮递员必须按照清单上的镇子顺序送快递(镇子可能重复,并且对于重复的镇子不允许一次性处理,比如ABCB的话B一定要按顺序走两次才行)。

    测试数据有多组:

    N M

    x1 y1 t1 sl1

    x2 y2 t2 sl2

    xM yM tM slM

    R

    z1 z2zR

    N (2 ≤ N ≤ 200) 是镇子的数量,M (1 ≤ M ≤ 10000) 是旱路和水路合计的数量。从第2行到第M + 1行是路径的描述,路径连接xi yi两地,路径花费 ti (1 ≤ ti ≤ 1000)时间,sli 为L时表示是旱路,S时表示是水路。可能有两条及以上路径连接两个镇子,并且路径都是双向的。

    M + 2行的R是利腾需要去的镇子的数量,M + 3是利腾需要去的镇子的编号。

    初始状态利腾和船都在第一个镇子,且肯定有方法达到需要去的镇子。

    测试数据为0 0的时候表示终止。

    思路:

    先用Floyd预处理出单独走水路s或陆路l的两两之间的最短路。

    d[i][j]表示到序列第i点时船停在j,已经去了第i个镇子后,船停在第j个镇子里的状态下的最短路。

    状态转移:人从a到b,船从c到d,若船动加上l[a,c]+s[c,d]+l[d,b],若船不动则只要加上l[a][b]。

    然后ijk三重循环更新dp,其中递推公式思路:

    在推导ik的时候,定义一个中间状态j表示先从i-1走旱路到j,然后从j走水路去k,最后从k走旱路去i,于是就把船扔在了k。

    如果j==k的时候就不需要绕圈子了。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    int s[210][210];//水路
    int l[210][210];//陆路
    int d[1010][210];//dp
    int b[1010];//目标路线
    
    const int INF = 0x3f3f3f3f;
    
    void Floyd(int n) 
    {
    	for (int k = 1; k <= n; ++k) 
    	{
    		for (int i = 1; i <= n; ++i) 
    		{
    			for (int j = 1; j <= n; ++j) 
    			{
    				s[i][j] = min(s[i][j], s[i][k] + s[k][j]);
    				l[i][j] = min(l[i][j], l[i][k] + l[k][j]);
    			}
    		}
    	}
    }
    
    int main() 
    {
    	int n, m, r;
    	int u, v, c;
    	char tp[2];
    
    	while (scanf("%d%d", &n, &m) != EOF && n) 
    	{
    		memset(s, INF, sizeof(s));
    		memset(l, INF, sizeof(l));
    		memset(d, INF, sizeof(d));
    
    		while (m--) 
    		{
    			scanf("%d%d%d%s", &u, &v, &c, tp);
    			if (tp[0] == 'L') 
    			{
    				l[u][v] = min(l[u][v], c);
    				l[v][u] = min(l[v][u], c);
    			}
    			else 
    			{
    				s[u][v] = min(s[u][v], c);
    				s[v][u] = min(s[v][u], c);
    			}
    		}
    
    		for (int i = 1; i <= n; ++i) 
    		{
    			l[i][i] = 0;
    			s[i][i] = 0;
    		}
    
    		scanf("%d", &r);
    
    		for (int i = 1; i <= r; ++i) 
    		{
    			scanf("%d", &b[i]);
    		}
    
    		Floyd(n);
    
    		for (int i = 1; i <= n; ++i) 
    		{
    			d[1][i] = min(d[1][i], s[b[1]][i] + l[i][b[1]]);
    		}
    
    		for (int i = 1; i <= r; ++i) 
    		{
    			for (int j = 1; j <= n; ++j) 
    			{
    				//if (l[j][b[i]] >= INF) continue;  //剪枝
    				for (int k = 1; k <= n; ++k) 
    				{
    					//if (d[i - 1][k] >= INF) continue;  //剪枝
    					if (j == k) 
    					{
    						if (l[b[i - 1]][b[i]] < INF) 
    						{
    							d[i][j] = min(d[i][j], d[i - 1][k] + l[b[i - 1]][b[i]]);
    						}
    					}
    					else 
    					{
    						//三个INF相加可能上溢
    						if (l[b[i - 1]][k] < INF && s[k][j] < INF && l[j][b[i]] < INF) 
    						{
    							d[i][j] = min(d[i][j], d[i - 1][k] + l[b[i - 1]][k] + s[k][j] + l[j][b[i]]);
    						}
    					}
    				}
    			}
    		}
    
    		int ans = INF;
    		for (int j = 1; j <= n; ++j) 
    		{
    			ans = min(ans, d[r][j]);
    		}
    		printf("%d
    ", ans);
    	}
    	return 0;
    }
  • 相关阅读:
    【POJ3237】Tree 树链剖分+线段树
    【BZOJ3531】[Sdoi2014]旅行 树链剖分+动态开点线段树
    【BZOJ4034】[HAOI2015]树上操作 树链剖分+线段树
    【BZOJ1984】月下“毛景树” 树链剖分+线段树
    【BZOJ2243】[SDOI2011]染色 树链剖分+线段树
    【BZOJ1036】[ZJOI2008]树的统计Count 树链剖分
    树形DP水题杂记
    【BZOJ1827】[Usaco2010 Mar]gather 奶牛大集会 树形DP
    【BZOJ1864】[Zjoi2006]三色二叉树 树形DP
    【BZOJ1060】[ZJOI2007]时态同步 树形DP
  • 原文地址:https://www.cnblogs.com/demian/p/7396472.html
Copyright © 2011-2022 走看看