题目概述
有
n
个点,定义
解题报告
暴力想法是去掉一个点,然后刷Floyd,效率是
O(n4)
,无法承受。
考虑分治,设当前区间为
[L,R]
,拆为
[L,mid]
和
[mid+1,R]
:
1.选择
[L,mid]
,那么就用Floyd将
[L,mid]
加入状态,然后分治
[mid+1,R]
。
2.选择
[mid+1,R]
,那么就用Floyd将
[mid+1,R]
加入状态,然后分治
[L,mid]
。
当
L=R
时,说明不选的节点就是
L(R)
,累计答案。
这道题很好的体现出了Floyd的插点性质。
示例程序
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=300;
int n,dis[maxn+5][maxn+5];
LL Solve(int L,int R)
{
LL ans=0;
if (L==R)
{
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (i!=L&&j!=L) ans+=dis[i][j];
return ans;
}
int tem[maxn+5][maxn+5],mid=L+(R-L>>1);
memcpy(tem,dis,sizeof(dis));
for (int k=mid+1;k<=R;k++)
for (int i=1;i<=n;i++) if (i!=k)
for (int j=1;j<=n;j++) if (i!=j&&j!=k)
if (~dis[i][k]&&~dis[k][j])
{
if (~dis[i][j]) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); else
dis[i][j]=dis[i][k]+dis[k][j];
}
ans+=Solve(L,mid);memcpy(dis,tem,sizeof(tem));
for (int k=L;k<=mid;k++)
for (int i=1;i<=n;i++) if (i!=k)
for (int j=1;j<=n;j++) if (i!=j&&j!=k)
if (~dis[i][k]&&~dis[k][j])
{
if (~dis[i][j]) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); else
dis[i][j]=dis[i][k]+dis[k][j];
}
ans+=Solve(mid+1,R);memcpy(dis,tem,sizeof(tem));
return ans;
}
int main()
{
freopen("program.in","r",stdin);
freopen("program.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
scanf("%d",&dis[i][j]);
printf("%lld\n",Solve(1,n));
return 0;
}