都市环游

【问题描述】

因为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;
}
原文地址:https://www.cnblogs.com/yanshannan/p/7392296.html