[Acwing] 345. 牛站 Floyd + 快速幂倍增

前言

快速幂思想还是不怎么会活用啊,我怎么就写题解了呢

又打算什么时候补呢?为什么不是今天就解决了呢?

又要拖到什么时候呢?
传送门 :

思路

这题一开始有一点像 B e l l m a n Bellman Bellman 但是 B e l l m a n Bellman Bellman不超过k

条边的最短路(虽然看到题解有人用这个求出来,但是我不会 😦

因此这题和 B e l l m a n Bellman Bellman不一样

我们令 F l o y d Floyd Floyd
状态表示 : f [ k , i , j ] f[k,i,j] f[k,i,j]从点 i i i到点 j j j经过恰好 k k k条边的最短路
状态计算 : f [ a + b , i , j ] = m i n ( f [ a , i , j ] + f [ b , i , j ] ) f[a+b,i,j] = min(f[a,i,j]+f[b,i,j]) f[a+b,i,j]=min(f[a,i,j]+f[b,i,j])

但是显然,我们不能通过枚举每一个分界点 k k k来做出这题

但是我们可以通过被整的做法:例如K =38 ( 100110 ) 2 (100110)_2 (100110)2,我们只需要从 2 , 4 , 32 这 三 个 答 案 转 移 过 来 就 行 了 2,4,32这三个答案转移过来就行了 2,4,32 因此处理如下

CODE

int k,m,S,E;

int g[N][N] ; 
int res[N][N];

map<int,int> ids;
int n;
void mul(int a[][N],int b[][N],int c[][N])
{
	int temp[N][N];
	memset(temp,0x3f,sizeof temp);
	
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
			{
				temp[i][j] = min(temp[i][j],b[i][k]+c[k][j]);
				
			}
	memcpy(a,temp,sizeof temp);
	
}
void qmi()
{
	memset(res,0x3f,sizeof res);
	for(int i=1;i<=n;i++)
	res[i][i] = 0 ;
	
	while(k)
	{
		if(k&1)
		mul(res,g,res);
		mul(g,g,g);
		k>>=1;
	}
}
void solve()
{
	cin>>k>>m>>S>>E;
	//答案边数 , 图边数, 起点,终点
	
	//离散化
	if(ids.count(S) == 0) ids[S] = ++n;
	if(ids.count(E) == 0) ids[E] = ++n;
	
	memset(g,0x3f,sizeof g);
	S = ids[S];E = ids[E];
	
	while(m -- )
	{
		int a,b,c;cin>>c>>a>>b;
		if(ids.count(a) ==  0) ids[a] = ++n;
		if(ids.count(b) ==  0) ids[b] = ++n;
		a = ids[a],b = ids[b];
		g[a][b] =  g[b][a] = min(g[a][b],c); 
	}
	
	//通过快速幂求经过 K条边
	qmi();
	cout<<res[S][E]<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值