最短路问题(1)

来源:互联网 发布:js数组对象去重复 编辑:程序博客网 时间:2024/06/06 05:51

  从图中指定的一点出发走到某一目标点如果存在多种不同的走法,

最短的是哪条路?其长度是多少?

图论中解决上述问题的方法都属于最短路算法。由于图的特点不同、

存储结构不同、确立算法的侧重方向不同,所以算法是多种多样的。

一 无权图及树网

在无权图中,路径长度只与路径上的点数有关,而与路径上的边权和点权无关。

例如广搜那节课例题2“方格图上求从S到E的最短路”一题。

不少多维(多分量)的图(包括许多抽象图)的最短路都属于此类。

由于广搜就是逐层展开的,所以很适合解决此类问题。

例题1 跳棋

  某种跳棋共有5个格子排成一排,

其中两格放黑子、两格放白子,还有一格为空。

跳棋规则允许紧邻空格的棋子走入空格,还允许把一个棋子隔着另一个棋子跳入空格。

求把棋子的一种分布状态变成另一种分布状态至少经过多少步?

先输入一个长度5且含有两个1、两个2、一个0的数字串,表示原来的分布状态,

再输入另一个长度5且含有两个1、两个2、一个0的数字串,表示后来的分布状态。

输出就是最少步数。

分析

  把5个位置看成“5维空间(5个分量决定)中的点”,问题就是求从出发点到目标点的最少步数了。

由于图中的点可能被重复搜索,所以需要标记搜过的点。(哈希登记)

又由于每个点的步数都可以从其上游一点的步数加1求得,所以应当建立所有点的步数表。

事实上,上述两个表可以合二而一。参考代码如下:

#include <iostream>

#include <cmath>

#include <cstring>

#include <queue>

using namespace std;

int s[3][3][3][3];

string a,b,c;

queue<string> q;

void bfs()

{

         inti,k;

         q.push(a);

         s[a[0]-'0'][a[1]-'0'][a[2]-'0'][a[3]-'0']=0;

         for(i=0;i<4;++i)b[i]-='0';

         while(!q.empty()&& s[b[0]][b[1]][b[2]][b[3]]<0)

         {

                   a=q.front();//取出队头元素

                   k=a.find("0");

                   for(i=0;i<5;++i)

                            if(k!=i&& abs(k-i)<3)  //i不是空的点,且i和k距离不太大,可跳或可走

                            {

                                     c=a;

                                     swap(c[k],c[i]);

                                     if(s[c[0]-'0'][c[1]-'0'][c[2]-'0'][c[3]-'0']<0)

                                     {

                                     q.push(c);                                               

                                     s[c[0]-'0'][c[1]-'0'][c[2]-'0'][c[3]-'0']=s[a[0]-'0'][a[1]-'0'][a[2]-'0'][a[3]-'0']+1;

                                     }

                            }

                   q.pop();

         }

}

int main()

{

         memset(s,-1,sizeof(s));

         cin>>a>>b;

         bfs();

         cout<<s[b[0]][b[1]][b[2]][b[3]]<<endl;

         return0;

}

由于在树状结构中,任意两点间的路径是唯一的,因此,广搜可以求得最短路。

它既是最短路也是最长路。

 

练习

1 倒米

3个储存米的容器容量分别是3、5、8升,

如果分别放有1、2、5升米,最少倒几次米能评分为两个4升?倒几次能倒成2、5、1升?

输入第一行3个整数分别代表3、5、8升容器中原有米数,

第二行3个整数分别代表3、5、8升容器中希望的米数。

如果倒得成就输出最少倒米次数,否则输出-1.

样例输入

0 2 7

3 1 5

输出 2

样例说明:

0 2 7倒成 0 1 8 倒成 3 1 5

 

2 九宫格游戏

3行3列9个格子中分别放着1到8的8个数字另有一个空格。

每次可把紧邻空格的数字移入空格,至少移动多少次才能变为目标分布?

如果无法实现输出-1,否则输出最少移动次数。

输入3个长度为3的数字串表示原来的分布,

在输入3个长度为3的数字串表示后来的分布。

0表示空格。

 

3 NOIP提高组2002 字串变换

 

4 NOIP提高组2007 树网的核

 

5 海淀2013 拉灯

拉灯游戏 lamp.cpp|c|pas

  n盏灯从左到右放成一排,每盏灯由一个拉线开关来控制。

开始灯都未亮,求在满足下面拉灯规则的前提下,把所有灯都拉亮的最少拉灯线次数。

规则

  拉第i盏灯的灯线必须同时满足下面两个前提条件:

① 若i>1,则第i-1盏灯必须是灭着的。

② 若i>2,则号数不大于i-2的灯都是亮着的。

输入文件名为lamp.in中仅有一个正整数n,代表灯盏总数。

输出文件lamp.out中也仅有一个整数表示最少拉灯线次数。

输入输出样例

lamp.in

3

lamp.out

5

样例说明:

原来3盏灯的状态          灭灭灭

拉1灯变成    亮灭灭

拉3灯变成    亮灭亮

拉1灯变成    灭灭亮

拉2灯变成    灭亮亮

拉1灯变成    亮亮亮

数据范围:

80%的数据满足n<20

90%的数据满足n<30

100的数据满足n<100

 

选作NOIP2013DAY2 华容道

 

二 可拓扑图

1 直接计算 例如NOIP普及组2004 花生采摘

2 动归 例如跳马、飞翔、传递物品等

最长单调子序列的平方算法也可看做此类算法,它自然而然就是一个拓扑序列,

所以按拓扑顺序动归很容易进行。

由于无圈,所以地推关系得以实现。

例题2 海淀2010竞赛 限体力上楼梯

三 上楼梯 程序文件主名stairs

  明明上n级台阶可用4种步幅,当然每种步幅花费的体力也不一样,对应关系如下:

    步幅大小    体力花费

    1       1

    2       3

    3       6

    4       10

明明开始共有m个体力,求他最少要跨多少步才能上完所有台阶?

输入文件名为stairs.in。其中只有n和m两个正整数,中间用空格做间隔符。0<n<=m。

对于30%的数据,m<100

对于60%的数据,m<10000

对于80%的数据,m<1000000

对于100%的数据,m<10^19

输出文件名为stairs.out,其内容只有题目所求的最少步数这一个数。

输入输出样例

stairs.in的内容为:

3 5

则stairs.out的内容为:

2

分析 此图显然拓扑,而且属于最短路类型。

可设f[i][j]为用j个体力上i级台阶的最少步数

#include<iostream>

#include<fstream>

using namespace std;

ifstream fin("stairs.in");

ofstream fout("data.out");

//f[louti][tili]=最少步数

int f[4010][10000];

int main()

{

   int i,j,n,m,k,s;

   memset(f+1,127,sizeof(f)-40000);

   fin>>n>>m;

 s=n+1;

  intt[5]={0,1,3,6,10};

 for(i=0;i<n;i++)

   for(j=i;j<m;j++)

    for(k=1;k<=4;k++)

     if(f[i][j]+1<f[i+k][j+t[k]])

        f[i+k][j+t[k]]=f[i][j]+1;

 for(j=n;j<=m;j++)

   s=min(s,f[n][j]);

   fout<<s<<endl;

   return 0;

}

 

 

练习

6 1变n的最小代价

  如果整数c=a*b,

则c可变成c+a,代价是b。

c也可变成c+b,代价是a。

求从1变成n的最小总代价。

输入正整数n,n<200000

输出最小总代价。

 

7 NOIP提高组2009 乌龟棋

 

8 NOIP提高组2005 过河

9 跳马三问

在一个很大(坐标不限、可正可负)的棋盘上,马从A行B列出发,

只许向右跳(右上或右下不限),

求跳到C行D列最少跳多少部?最多多少部?共有多少种不同跳法?

输入A、B、C、D4个整数,

0<a<c<100;0<b,d<100

 

10 三维导弹拦截

bomb

题目描述

一场战争正在A国与B国之间如火如荼的展开。

         B国凭借其强大的经济实力开发出了无数的远程攻击导弹,B国的领导人希望,通过这些导弹直接毁灭A国的指挥部,从而取得战斗的胜利!当然,A国人民不会允许这样的事情发生,所以这个世界上还存在拦截导弹。

         现在,你是一名A国负责导弹拦截的高级助理。

         B国的导弹有效的形成了三维立体打击,我们可以将这些导弹的位置抽象三维中间的点(大小忽略),为了简单起见,我们只考虑一个瞬时的状态,即他们静止的状态。

         拦截导弹设计非常精良,可以精准的引爆对方导弹而不需要自身损失,

但是A国面临的一个技术难题是,这些导弹只懂得直线上升。

精确的说,这里的直线上升指xyz三维坐标单调上升。

         给所有的B国导弹按照1至n标号,一枚拦截导弹可以打击的对象可以用一个xyz严格单调上升的序列来表示,例如:

B国导弹位置:(0, 0, 0) (1, 1, 0) (1, 1, 1), (2, 2, 2)

一个合法的打击序列为:{1, 3, 4}

一个不合法的打击序列为{1, 2, 4}

         A国领导人将一份导弹位置的清单交给你,并且向你提出了两个最简单不过的问题(假装它最简单吧):

求1枚拦截导弹最多可以摧毁多少B国的导弹?

不管是为了个人荣誉还是国家荣誉,更多的是为了饭碗,你,都应该好好的把这个问题解决掉!

输入文件

第一行一个整数n给出B国导弹的数目。

         接下来n行每行三个非负整数Xi, Yi, Zi给出一个导弹的位置,你可以假定任意两个导弹不会出现在同一位置。

输出文件

         输出文件仅有1行。输出一个整数P,表示一枚拦截导弹之多能够摧毁的导弹数。

样例输入

4               

0 0 0         

1 1 0

1 1 1

2 2 2

样例输出

         3

数据约定

所有的坐标都是[0,106]的整数

对于30%的数据满足n  31

对于50%的数据满足n  101

对于100%的数据满足n  1001

 

0 0
原创粉丝点击