都市环游
来源:互联网 发布: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;}
- 都市环游
- 都市环游
- 都市环游
- 都市环游
- 环游世界
- 迷茫都市
- 都市流浪者
- 都市精英
- 佛教都市
- 第九都市
- ---45块环游世界---
- 编码巧解称环游戏
- 免费环游世界
- 编码巧解称环游戏
- 飞屋环游记
- 飞屋环游记
- 暖暖环游世界
- 环游四边形 高精度
- mysql按天,月,年等时间查询数据
- Linux 求文件交集 差集等
- 11
- spring装配与Autowired自动装备
- JavaScript : 命名
- 都市环游
- zoj 3605 Find the Marble (dp)
- Java 工具类
- 线性表—单向链表
- java的第一次!!!!!
- Sublime Text格式化Xml
- linux搜索命令
- 数据结构学习心得——复习数据结构的原因
- 微信支付解决方案