SCAU周训8-C:计蒜客 - A1108

1.题目描述:

2.题意:
给出一个图,求所有刨去某个点的最短路径的权值和。

3.思路:
1)暴力。【O(n4),TLE】。直接在循环1->k里跑floyd。
2)分治+floyd。 【O(n3logn),AC】。这个思路我还得学个几百年。 直接求循环肯定是T的,300也承受不来4次方的摧残啊!我们来想想,每次挖掉一个点,对图重新求一遍最短路,其实有非常多的边的信息是不变的。这个重复计算我们可以去掉!那么我们该怎么利用上之前的信息呢?!分治这个时候就给了我们一个思路,我们可以先算一部分的,然后把这一部分用来给下一部分用。我们假设把点的区间分成两半:[l,m],[m+1,r];那么我们可以先算又半区间的值,然后用已经算好的值,再去更新左半边的值,因为我们已经有了经过右边点的值了,那么我们不经过的点只能从左边找,越分治,点的范围就越小;当点缩到只剩一个的时候,我们就可以直接挖掉这个点了。【有点像归并排序到最后,这个点的信息我们就可以选择性的返回。而这个就是,这个点的信息,选择性不要。】
在分治完左边之后,我们还原数组,继续去用左边区间的值去更新右边,这样一来两边的值我们都知道了,这个区间里面的点就ok了,接下来就是恢复数组,然后把答案转交给更大的区间啦!

4.代码:

//C
#include
#define rep(i,a,b) for(int i=(a);i<(b);++i)
using namespace std;

typedef long long ll;

const int maxn=3e2+5;
const int inf =0x3f3f3f3f;

inline ll read()
{
	ll x=0,sign=1;char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') sign=-1;
	for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
	return x*sign;
}

int n,g[maxn][maxn];
ll cdq(int l,int r)
{
	ll res=0;
	if(l==r)
	{
		rep(i,1,n+1)
			rep(j,1,n+1)
				if(i^l&&j^l)
					res+=g[i][j]==inf?-1:g[i][j];
		return res;
	}
	int m=l+r>>1,tmp[maxn][maxn];
	memcpy(tmp,g,sizeof(g));
	rep(k,m+1,r+1)
		rep(i,1,n+1)
			rep(j,1,n+1)
				g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
	res+=cdq(l,m);
	memcpy(g,tmp,sizeof(tmp));
	rep(k,l,m+1)
		rep(i,1,n+1)
			rep(j,1,n+1)
				g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
	res+=cdq(m+1,r);
	memcpy(g,tmp,sizeof(tmp));
	return res;
}

inline void solve()
{
	n=read();
	rep(i,1,n+1)
		rep(j,1,n+1)
		{
			int x=read();
			g[i][j]=!(~x)?inf:x;
		}
	ll res=cdq(1,n);
	printf("%lld",res);
}

int main()
{
	solve();
	return 0;
}

你可能感兴趣的:(SCAU周训)