都市环游

来源:互联网 发布:2017阿里云客服考试 编辑:程序博客网 时间:2024/04/27 16:37

【问题描述】

因为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("%d\n",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\n",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;}
原创粉丝点击