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;
}