华师一附中OI组

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 2097|回复: 6
打印 上一主题 下一主题

城市交通(减半路径)

[复制链接]

738

主题

1485

帖子

5420

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5420
跳转到指定楼层
楼主
发表于 2018-4-16 10:36:28 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
某城市有N(1<=N<=50)个街区,某些街区由公共汽车线路相连,如在图1中,街区1,2有一条公共汽车线路相连,且由街区1至街区2的时间为34分钟。由于街区与街区之间的距离较近,与等车时间相比可忽略不记,所以这个时间为两趟公共汽车的间隔时间,即平均的等车时间。
由街区1至街区5的最快走法为1-3-5,总时间为44分钟。
现在市政府为了提高城市交通质量,决定加开M(0<=M<=10)条公共汽车线路。若在某两个街区A,B之间加开线路(前提是A、B之间必须已有线路),则从A到B的旅行时间缩小为原来的一半(距离未变,只是等车的时间缩短了一半)。例如,若在1,2之间加开一条线路,则时间变为17分钟,加开两条线路,时间变为8.5分钟,以此类推。所有的线路都是环路,即如果由1至2的时间变为17分钟,则由2至1的时间也变为17分钟。
求加开某些线路,能使由城市1至城市N的时间最少。例如,在图1中,如果M=2,则改变1-3,3-5的线路,总的时间可以减少为22分钟。

输入文件名为City.In。
第一行为城市数N与加开线路数M。
第二行至第N+1行,每行为N个实数,第I+1行第J列表示由城市I到城市J的时间。
如果时间为0,则城市I不可能到城市J。
注意:输入数据中,从城市1到城市N至少有一条路线。


输出文件名为City.Out.一行为由城市1到城市N的最小时间X(保留小数点后两位)。

样例输入

City.In
5 2
0 34 24 0 0
34 0 10 12 0
24 10 0 16 20
0 12 16 0 30
0 0 20 30 0

样例输出
City.Out
22.00
  

  
数据规模

20%的数据 M=0
20%的数据 M=1
100%的数据 0<=M<=10 1<=N<=50

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

738

主题

1485

帖子

5420

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5420
沙发
 楼主| 发表于 2018-4-16 10:44:05 | 只看该作者
此题是我改自2000年NOI国家集训队同学的原创试题,用来作为我每年的最短路径的训练题。他要求我们熟练掌握DK方法或者FLOYD方法。此体的解法是很多,每种做法大家都可以尝试一下,训练自己。
1、DK是用一个值记录当前点到起点的最短路径,更新与当前点相连的所有点的最短路径,然后在所有的未标记的点里面去找最短路径,再去更新,那么我们可以加上一个多维数组,每次更新好几个点行不行呢?
2、分层图
3、FLOYD改进
回复 支持 反对

使用道具 举报

738

主题

1485

帖子

5420

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5420
板凳
 楼主| 发表于 2018-4-16 11:53:13 | 只看该作者
如果只考虑从城市1到城市N的最少时间,那么,这个问题就是一道求最短路径的问题。求最短路径问题在图论中是一道很基本的试题,有很多方法可以解决。这个问题,开始就给人一种熟悉的感觉,而实质呢?问题的“变数”在于“市政府”的改革措施很奇特,即每加一条公共汽车路线,则两个街区之间的旅行时间就缩短为原来的一半。这就给人一种“无处着手”的感觉。显然,这个改革不能分步执行,即它不具有“贪心法”的要求,而如果用搜索的方法解决,时间会很庞大。

在求两点之间的最短路径时,有一种比较优秀的方法:“Floyd-Warshall”算法,其算法的中心思想是这样的:
求A至B的满足中间结点不超过K的最短路径。而后,随着K逐渐增加至N而求出A至B的最短路径。则其算法的结构如下:
    For  K=1  To  N     {K从1增至N}
      For   A=1  To  N   Do
          For   B=1  To   N   Do    {求每一对结点的中间结点不超过K的最短路径}
      IF  (Map[A,K]>0) And (Map [K,B]>0)   Then
                                       {如果A与K,K与B均可达}
            IF (Map[A,B]=0)  Or  (Map[A,B]>Map[A,K]+Map[K,B]) Then
              {如果A、B不可达,或者A、B之间的路径不如A-K-B这条路径短,则改变Map[A,B]的值}
              Map[A,B]:=Map[A,K]+Map[K,B];

        注:Map[A,B]为从A到B的最短路的距离,如果A,B不可达,则Map[A,B]=0。

这个算法实质上就是一个“动态规划”算法。它是以最短路中间结点的取值来划分阶段的。第K个阶段为所有最短路的中间结点<=K时的情况。而第K个阶段只与前(K-1)个阶段有关系,所以,这个问题同时满足“无后效性”与“最优子问题”这两个性质,这个“动态规划”算法是正确的。
而在求改进M条边使最短路下降最快中,我们同样可以发现:
1:将道路A-B改造M条边可以分为如下两个问题(如果K是最短路上的一点)
      一:求A-K改造T条边;
      二:求K-B改造M-T条边。

T可以取从0至M的任意值。问题A-B改造M条边的最优解取决与这两个子问题的最优解。
2:在求M条边的过程中,始终只与改造T与M-T条边的问题发生联系。

以上两个特点即为“动态规划”的“无后效性”与“最优子问题”两个性质。所以,这个“城市交通”问题的算法为“动态规划”算法。

     具体如下:
       设Map[A,B,M]的值为从A到B且改造M条边的最短路长度,则:
   Map[A,B,M]=Min{Map[A,K,T]+Map[K,B,M-T]}
      其中T为从0到M中的一个数,K为A到B中间的一点。

     这个问题在确定K是与Floyd算法相同,所以这个问题实质上是一个“双重动态规划”问题。具体算法如下:
     For   G:=1  To   M    Do    {分M个阶段来解这个问题}

    For   K:=1  To   N  Do
           For   I:=1  To   N   Do
             For   J:=1   To  N   Do
                                                        {Floyd算法}
                Begin
                                           For  T:=0   To   G   Do
                                                If   Map[I,K,T]+Map[K,J,G-T]<Map[I,J,G] Then
                                                        Map[I,J,G]:=Map[I,K,T]+Map[K,J,G-T];
                                End;
说明:这个问题的Map数组的初始值与上一个Floyd算法不一样,初始值均为MaxInt。
     这个算法的时间复杂度为O(N^3*M^2),约为O(N^5),还是一个多项式级的算法。
总结
     这个问题虽然借助图的模型,但实质上是一个“动态规划”的问题。这个题目主要的意义在于:图论中的很多算法都是有它自己的背景的,我们应该深入考虑算法的根本所在。Floyd算法的实质思想就是“动态规划”,它虽然在图论中呈现出巨大的作用,但其“动态规划”的结构、原理还是应该“独立而论”的。本题就是深入挖掘了Floyd算法的“动态规划”原理,并在其基础上提出的“双重动态规划”问题。
回复 支持 反对

使用道具 举报

738

主题

1485

帖子

5420

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5420
地板
 楼主| 发表于 2018-4-16 12:03:35 | 只看该作者
TOP1 WWX的做法,分层图,请他自己上程序,说明和注释
回复 支持 反对

使用道具 举报

738

主题

1485

帖子

5420

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5420
5#
 楼主| 发表于 2018-4-16 12:17:32 | 只看该作者
喻天成的做法,简单而且粗暴。注意其中不联通的在处理。
  1. #include<iostream>
  2. #include<cstdio>
  3. #include<algorithm>
  4. #include<queue>
  5. #include<iomanip>
  6. using namespace std;
  7. double f[55][55][15];
  8. int m,n;
  9. const int inf=999999;
  10. int main()
  11. {
  12.     freopen("city.in","r",stdin);
  13.     freopen("city.out","w",stdout);
  14.     cin>>n>>m;
  15.     for(int i=1; i<=n; i++)
  16.         for(int j=1; j<=n; j++)
  17.         {
  18.             cin>>f[i][j][0];
  19.             if(i!=j&&!f[i][j][0]) f[i][j][0]=inf;
  20.         }
  21.     for(int k=1; k<=m; k++)
  22.         for(int i=1; i<=n; i++)
  23.             for(int j=1; j<=n; j++)
  24.                 if(f[i][j][0]<inf)
  25.                     f[i][j][k]=f[i][j][k-1]/2;
  26.                 else f[i][j][k]=inf;

  27.     for(int x=0; x<=m; x++)
  28.         for(int k=1; k<=n; k++)
  29.             for(int i=1; i<=n; i++)
  30.                 for(int j=1; j<=n; j++)
  31.                     for(int y=0; y<=x; y++)
  32.                         if(i!=j&&j!=k&&j!=i)
  33.                             f[i][j][x]=min(f[i][k][y]+f[k][j][x-y],f[i][j][x]);
  34.     cout<<fixed<<setprecision(2)<<f[1][n][m];
  35.     return 0;
  36. }
复制代码
回复 支持 反对

使用道具 举报

5

主题

21

帖子

63

积分

华一学生

积分
63
6#
发表于 2018-4-18 13:12:26 | 只看该作者
我是利用分层图跑到Dij
用dis[i][j]表示到第i剪了j次的最短距离,然后可以很容易的进行转移
复杂度为k*n^2
回复 支持 反对

使用道具 举报

5

主题

21

帖子

63

积分

华一学生

积分
63
7#
发表于 2018-4-18 13:13:23 | 只看该作者
# include<iostream>
# include<cstdio>
# include<cmath>
# include<cstdio>
# include<cstring>
# include<iomanip>
using namespace std;
const int mn = 55;
const long double inf = 0xffffffffff;
inline long double minn(long double x,long double y)
{
    return x>y ? y : x;
}
long long a[mn][mn];
int n,m;
long double dis[mn][15];
bool vis[mn];
int main()
{
    freopen("city.in","r",stdin);
    freopen("city.out","w",stdout);
    std::ios::sync_with_stdio(false);
    //scanf("%d%d",&n,&m);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
       cin>>a[i][j];
    for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++)
        dis[i][j]=inf;
    for(int i=0;i<=m;i++)
        dis[1][i]=0;
    for(int k=0;k<=m;k++)
    {
         int s=2;
         memset(vis,0,sizeof(vis));
         while(s>0)
         {
            double mn=inf+1;
            s=-1;
            for(int i=1;i<=n;i++)
            {
               if(dis[i][k]<mn && !vis[i])
               {
                   mn=dis[i][k],s=i;
               }
            }
            if(s==-1)
              break;
            vis[s]=1;
            for(int j=1;k+j<=m;j++)
                dis[s][j+k]=minn(dis[s][k],dis[s][k+j]);
            for(int i=1;i<=n;i++)
            {
                if(a[s][i]!=0)
                {
                    for(int j=1;k+j<=m;j++)
                    {
                        dis[i][k+j]=minn(dis[i][k+j],dis[s][k]+a[s][i]*1.0/(1<<j)*1.0);
                    }
                    dis[i][k]=minn(dis[i][k],dis[s][k]+a[s][i]);
                }
            }
         }
    }
    //printf("%.2lf",dis[n][m]);
    cout<<fixed<<setprecision(2)<<dis[n][m];
    /*for(int i=0;i<=m;i++)
        printf("%.2lf\n",dis[n][i]);*/
    return 0;
}
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|服务支持:DZ动力|华师一附中OI组  

GMT+8, 2024-11-2 14:22 , Processed in 0.106206 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表