【题解】Leyni的汽车比赛

【题解】Leyni的汽车比赛

HRBUST - 1404

思维题?居然被我凑出来了

这种图论题先设这样一个状态

\[ans(i,j,f) \]

表示从i到j,最多使用f个交通工具的最短路

转移的话,每辆车先自己跑一个Floyd,然后进去就好了,转移枚举中间点。

但是你说\(f\)很大,感觉这样做不行了,但是仔细思考一下,最多换\(n\)辆车就会到达终点,所以让\(f\)\(n\)取min就好了。

这种分析复杂度的方法很好用,还有一个更典型的例子是,一个区间询问题,区间长度是\(100\),询问是\(1e5\)的,但是你每次询问只会\(O(n^2)\)的暴力,怎么过这个subtask? 直接记忆化啊!总共就\(100\times 90\)个不同的区间啊

于是我们获得了一个\(O(n^5)\)的做法,我以为可以过,但是有多组数据,交一发T了

for(register int t=1;t<=m;++t)
     g[t].read(),g[t].gen();
for(register int t0=1;t0<=m;++t0)
     for(register int f=1;f<=n+2;++f)
          for(register int k=1;k<=n;++k)
               for(register int t=1;t<=n;++t)
                    for(register int i=1;i<=n;++i)
                         ans[t][i][f]=Min(ans[t][i][f],ans[t][i][f-1],ans[t][k][f-1]+g[t0][k][i],ans[t][k][f-1]+ans[k][i][1]);
for(register int t=1;t<=r;++t){
     register int t1=qr(),t2=qr(),t3=min(qr()+1,n+1);
     printf("%d\n",ans[t1][t2][t3]);
}

代码中\(g[]\) 是我定义的结构体,里面是一个\(e[maxn][maxn]\)\(g[t].gen()\)是跑一遍floyd

考虑优化一下,由于我们是要求最小,我们看一下混乱不堪的转移,考虑在Min函数的第三个参数动动手脚,实际上,对于一个确定\(f\),我们求一个最小的\(g[t0][k][i]\)就好了,我们随便找个东西存一下就完事了。

实际上我这份转移靠感觉的代码做了这件事情,就是\(ans[t][k][f-1]+ans[k][i][1]\),这里\(ans[k][i][1]\)实际上就记录了最小的\(g[t0][k][i]\)。所以我们少枚举一个\(t0\),复杂度变为\(O(n^4)\)

本来这种思维题我是做不出来的,但d 是为啥我又写出来了?因为我发现可以通过观察代码的行为来优化算法。。。我用这种技巧已经帮助我在模拟赛里多拿了好几百分了23333333

没有退役全靠它

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;  typedef long long ll;
inline int qr(){
      register int ret=0,f=0;
      register char c=getchar();
      while(c<48||c>57)f|=c==45,c=getchar();
      while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
      return f?-ret:ret;
}

const int maxn=51;
const int inf=0x3f3f3f3f;
int ans[maxn][maxn][53];
int n,m,r;
struct Graph{
      int e[maxn][maxn];
      inline void read(){
	    for(register int t=1;t<=n;++t)
		  for(register int i=1;i<=n;++i)
			e[t][i]=qr();
      }
      inline int* operator [](register int x){return e[x];}
      inline void gen(){
	    for(register int k=1;k<=n;++k)
		  for(register int t=1;t<=n;++t)
			for(register int i=1;i<=n;++i)
			      e[t][i]=min(e[t][i],e[t][k]+e[k][i]);
      }
      inline void print(){
	    for(register int t=1;t<=n;++t,printf("\n"))
		  for(register int i=1;i<=n;++i)
			printf("%d ",e[t][i]);
      }
}g[maxn];

inline int Min(const int&a,const int&b){if(a<b) return a;   return b;}
inline int Min(const int&a,const int&b,const int&c){return Min(a,min(b,c));}
inline int Min(const int&a,const int&b,const int&c,const int&d){return Min(a,Min(b,c,d));}

int main(){
      while(scanf("%d %d %d",&n,&m,&r)!=EOF){
	    for(register int t=0;t<=n;++t)
		  for(register int i=0;i<=n;++i)
			for(register int k=0;k<=n+2;++k)
			      ans[t][i][k]=inf;
	    for(register int t=1;t<=n;++t) ans[t][t][0]=0;
	    for(register int t=1;t<=m;++t){
		  g[t].read(),g[t].gen();
		  for(register int i=1;i<=n;++i)
			for(register int k=1;k<=n;++k)
			      ans[i][k][1]=min(ans[i][k][1],g[t][i][k]);
	    }
	    for(register int f=1;f<=n+2;++f)
		  for(register int k=1;k<=n;++k)
			for(register int t=1;t<=n;++t)
			      for(register int i=1;i<=n;++i)
				    ans[t][i][f]=Min(ans[t][i][f],ans[t][i][f-1],ans[t][k][f-1]+ans[k][i][1]);
	    for(register int t=1;t<=r;++t){
		  register int t1=qr(),t2=qr(),t3=min(qr()+1,n+1);
		  printf("%d\n",ans[t1][t2][t3]);
	    }
      }
      return 0;
}


posted @ 2019-08-25 20:36  谁是鸽王  阅读(159)  评论(0编辑  收藏  举报