zoukankan      html  css  js  c++  java
  • 都市环游

    【问题描述】

    因为SJY干的奇怪事情过多,SJY收到了休假的通知,于是他准备在都市间来回旅游。SJY有一辆车子,一开始行驶性能为0,每过1时间行驶性能就会提升1点。每个城市的道路都有性能要求。SJY一共有t时间休息,一开始他位于1号城市(保证1号城市道路要求为0),他希望在n号城市结束旅程。每次穿过一条城市间的路会花费1时间,当然他也可以停留在一个城市不动而花费1时间。当且仅当车子的行驶性能大于等于一个城市,我们才能到达那里。SJY希望知道,旅游的方案模10086后的答案。(只要在某一时刻通过的道路存在一条不相同,就算不同的方案)

    【输入】

    第一行三个数n,m,t,表示有n个城市m条道路t时间。
    第二行n个数,hi表示第i个城市的道路性能要求。
    

    第三到m+2行,每行两个数u,v,表示城市u与城市v之间有一条单向道路连接(可能有重边)。

    【输出】

    包括一个数字,表示旅游的方案模10086。

    【输入输出样例】

    travel.in
    5 17 7
    0 2 4 5 3
    1 2
    2 1
    1 3
    3 1
    1 4
    4 1
    4 5
    5 4
    5 3
    4 1
    2 1
    5 3
    2 1
    2 1
    1 2
    2 1
    1 3
    travel.out
    245

    【数据规模和约定】
    对于20%的数据,n<=10,t<=80;
    对于50%的数据,n<=30,t<=80;
    对于100%的数据,n<=70,m<=1000,t<=100000000,hi<=70。

    【题解】

    算法1:
    暴力 ,搜索全部路径,发现每一步的行动可以用一个转移矩阵表示,每一步相当于乘上矩阵。

    算法2:
    由算法1得到,将每一步移动的转移矩阵都求出来,然后进行矩阵乘法,时间复杂度 O(n^3*t)

    算法 3:
    在算法2的基础上利用矩阵快速幂优化矩阵乘法,时间复杂度O(n^3*logt)

    用邻接矩阵来存点与点之间的联通关系。
    那么对邻接矩阵进行矩阵乘法就可求出对应时刻的联通方法
    例如进行k次乘法,得到的S[1][n]就为k时刻1到n的走法总计
    具体来说,由矩阵乘法的运算规则可知,其实是找了一个中转k
    从i到j的方案即为从i到k的方案 乘以 从k到j的方案
    不断枚举k,即刚好满足矩阵乘法运算规则(也是乘法原理)
    本体前面的时间限制hi<=70较小,暴力(到处继承状态)即可。
    后面的用矩阵快速幂快速解决,
    最后答案存在S[1][n] (t>70) 或 f[t][n] (t<=70)中。

    想我考试时看到这道题竟无从下手,因为我不能手算出样例。。。但其实转念一想,手算不出就让计算机算啊,这可不是不敢尝试的理由。于是,在别人大暴力50分时我又爆0了。。。

    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #define fp(i,a,b) for(int i=a;i<=b;i++)
    #define fq(i,a,b) for(int i=a;i>=b;i--)
    #define il inline
    #define ll long long 
    using namespace std;
    const int N=75,M=5010,mod=10086;
    int n;
    bool vis[N];
    int a[M],b[M],f[N][N],h[N],id[N],g[N][N];//f[i][j]表示在第i秒时走到第j个城市的方案数,a、b表示一条边的两个端点,h表示一个城市的进入条件,id是城市编号
    struct Mat//矩乘新模板
    {
        int s[N][N];
        il void mul(Mat &y)
        {
            memset(g,0,sizeof(g));
            fp(i,1,n)
              fp(j,1,n)
                fp(k,1,n)
                (g[i][j]+=1ll*s[i][k]*y.s[k][j]%mod)%=mod;
            memcpy(s,g,sizeof(g));
        }
    }A,B;
    il bool cmp(int x,int y)
    {
        return h[x]<h[y];
    }
    il int gi()
    {
       int x=0;
       short int t=1;
       char ch=getchar();
      while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    int main()
    {
        freopen("travel.in","r",stdin);
        freopen("travel.out","w",stdout);
        int m,t,p=1;
        n=gi();m=gi();t=gi();
        fp(i,1,n) h[i]=gi(),id[i]=i;
        fp(i,1,m) a[i]=gi(),b[i]=gi();
        sort(id+1,id+1+n,cmp);//把点按进入的难易程度排序
        f[0][1]=1;
        fp(i,1,70)
        {
            while(p<=n&&h[id[p]]<=i) vis[id[p++]]=1;//把当前能进去的点打个标记
            fp(j,1,n) f[i][j]=f[i-1][j];//继承上一秒状态
          fp(j,1,m)
              if(vis[b[j]]) (f[i][b[j]]+=f[i-1][a[j]])%=mod;//如果当前点可进入,由他而到的目的地即可加上当前点的方案数
        }
        if(t<=70) {printf("%dn",f[t][n]);return 0;}//在70秒后,不再存在不可进入的点
        t-=70;
        fp(i,1,n) A.s[1][i]=f[70][i],B.s[i][i]++;//继承状态+加上停留方案
        fp(i,1,m) B.s[a[i]][b[i]]++;//加上可移动方案
        while(t)//快速幂模板
        {
            if(t&1) A.mul(B);
            B.mul(B),t>>=1;
        }
        printf("%d
    ",A.s[1][n]);
        fclose(stdin);
        fclose(stdout);
        return 0;
    }

    附上没用矩乘的大暴力,其实也不难:(!!!)

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int mod=10086;
    struct edge
    {
        int to,next;
    }a[2005];
    int head[75],cnt;
    int val[75];//城市的道路值
    int F[75][85];//记忆化dfs,记录走到第i个城市用了j时间后的方案数
    bool vis[75][85];//记录某一个状态是否访问过
    int n,m,t;
    int gi()
    {
        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 Add(int u,int v)
    {
        a[++cnt]=(edge){v,head[u]};
        head[u]=cnt;
    }
    int dfs(int u,int tot)//现在在城市u,用了时间tot
    {
        if (vis[u][tot]) return F[u][tot];
        int res=0;
        if (tot==t)
        {
            if (u!=n) res=0;
            else res=1;
        }
        else
            for (int e=head[u];e;e=a[e].next)
            {
                int v=a[e].to;
                if (tot+1>=val[v])
                res=(res+dfs(v,tot+1))%mod;
            }
        F[u][tot]=res;vis[u][tot]=1;
        return res;
    }
    int main()
    {
        freopen("travel.in","r",stdin);
        freopen("travel.out","w",stdout);
        n=gi();m=gi();t=gi();
        for (int i=1;i<=n;i++)
            val[i]=gi(),Add(i,i);
        for (int i=1;i<=m;i++)
        {
            int u=gi(),v=gi();
            Add(u,v);
        }
        printf("%d",dfs(1,0));
        return 0;
    }
  • 相关阅读:
    Linux 系统内核空间与用户空间通信的实现与分析
    busybox的一些变化
    switch_root 过程错误分析
    2 NICs on same subnet
    qt 维护x86和arm两套编译环境
    init脚本
    硬件测试
    devfs、sysfs、udev介绍
    原创整理:锐捷S3550系列交换机基本配置命令(二)
    【技术贴】red hat 9.0 找不到www.baidu.com.请检查名称并重试 net虚拟机设
  • 原文地址:https://www.cnblogs.com/yanshannan/p/7392296.html
Copyright © 2011-2022 走看看