Luogu P2886 [USACO07NOV]牛继电器Cow Relays|最短路,倍增

题目链接

题意:给出一张无向连通图,求S到E经过N条边的最短路。

数据范围:边数\(\le 100\),顶点编号\(\le1000\),\(N\le1 \times 10^6\)

题解:

最短路有三种解法,这种数据范围可使用\(floyd\)

可以用\(f[i][j][k]\)表示从\(i\)\(j\)经过\(k\)条边的最短路,显然TLE

考虑倍增。预处理\(K=2^k\),此时\(f[i][j][k]=min\{f[i][l][k-1] + f[l][j][k-1]\}\)

\(n\)二进制拆分,显然第\(x\)位为\(1\),就直接调用\(f[][][x]\)

然后用几乎同样的方法就能求出经过\(k\)条边的最短路了。

需要注意的是,按顶点跑\(floyd\)会超时,所以应该将其离散化。

上代码

#include<bits/stdc++.h>
#define s(S) 1-S%2
#define g(S) S%2
using namespace std;
const int oo=1000000000;
int n,t,s,e,u,v,c,maxp;
int f[1010][1010][20],ans[1010][1010][2],num[1010],snum;
int main()
{
	cin>>n>>t>>s>>e;
	num[s]=1;num[e]=2;snum=2;s=1;e=2;
	for (int i=1;i<=t;i++)
	{
		cin>>c>>u>>v;
		if (num[u]) u=num[u];else num[u]=++snum,u=num[u];
		if (num[v]) v=num[v];else num[v]=++snum,v=num[v];
		f[u][v][0]=f[v][u][0]=c;
		maxp=max(maxp,max(u,v));
	}	
	for (int k=0;(1<<k)<=n;k++)
	for (int i=1;i<=maxp;i++)
	  for (int j=1;j<=maxp;j++)
	  {
	    if (k!=0||((!f[i][j][k]))) f[i][j][k]=oo;
	  }
	 for (int k=1;(1<<k)<=n;k++)
	  for (int l=1;l<=maxp;l++)
	    for (int i=1;i<=maxp;i++)
	 	  for (int j=1;j<=maxp;j++)
	 	  {
	 		  f[i][j][k]=min(f[i][j][k],f[i][l][k-1]+f[l][j][k-1]);
	 	  }//预处理
	int S=0,sum=1,S1=0;
	while (n&&!(n&1))
	{
		n>>=1;S1++;
	}
	for (int i=1;i<=maxp;i++)
	 	for (int j=1;j<=maxp;j++)
	 	{
	 		ans[i][j][S]=f[i][j][S1];
	 	}
	n>>=1;S1++;
	while (n&&!(n&1))
	{
		n>>=1;S1++;
	}
	for (int i=1;i<=maxp;i++)
	  	for (int j=1;j<=maxp;j++)
	  	{
	  		ans[i][j][1]=oo;
	  	}
	while (n)
	{
		S++;S%=2;
		for (int l=1;l<=maxp;l++)
		for (int i=1;i<=maxp;i++)
	 	for (int j=1;j<=maxp;j++)
	 	{
	 		ans[i][j][S]=min(ans[i][j][S],ans[i][l][s(S)]+f[l][j][S1]);
	 	}
		n>>=1;S1++;
		while (n&&!(n&1))
		{
			n>>=1;S1++;
		}
	 	for (int i=1;i<=maxp;i++)
	  	for (int j=1;j<=maxp;j++)
	  	{
	  		ans[i][j][s(S)]=oo;
	  	}
	}//求ans,与上面几乎一致
	cout<<ans[s][e][S]<<endl;
	return 0;
}
posted @ 2019-08-02 09:52  fmj_123  阅读(162)  评论(0编辑  收藏  举报