给定一个n * n的矩阵 Cij ,需要构造出一个n * n的 Xij 矩阵,规定矩阵 Xij 只能由0或1组成,使得sum Cij * Xij 最小。
题意很明确,但是却很抽象,我们需要从图论的角度来看待三个初始条件:
1.X12+X13+…X1n=1
–>表示点1的出度为1
2.X1n+X2n+…Xn-1n=1
–>表示点n的入度为1
3.for each i (1<i<n), satisfies ∑Xki (1<=k<=n)=∑Xij (1<=j<=n).
–>如n==4时,X12+X22+X32+X42=X21+X22+X23+X24
X13+X23+X33+X43=X31+X32+X33+X34
–>表示点2~n-1的出度等于入度
初步分析得到三个条件可以等价于一条从1到n的最短路,权值为 Cij ,而 Xij 表示有向图中的边是否经过,1表示经过,0表示不经过因为在本题中权值非负,所以一定有一条最短路径满足题意。
但题意中没有说明点1的入度和点n的出度。那么意思就是他们可以各自组成一个环路(至少经过一个点,自环不满足题意,因为出度或入度为1)
假设的d[n]为1到n的最短路,c1为点1到点1的最短路,c2为点n到点n的最短路,所以我们最后要求的答案就是min(d[n],c1+c2)
#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const int maxn=300+7;
int a[maxn][maxn],n,s1,s2,d[maxn];
bool vis[maxn];
void spfa(int x){
queue<int>q;
for(int i=1;i<=n;i++){
if(i==x){
d[i]=inf;
vis[i]=0;
}
else{
d[i]=a[x][i];
q.push(i) ;
vis[i]=1;
}
}
while(!q.empty()){
int t=q.front(); q.pop();
vis[t]=0;
for(int i=1;i<=n;i++){
if(d[i]>d[t]+a[t][i]){
d[i]=d[t]+a[t][i];
if(!vis[i]){
q.push(i);
vis[i]=1;
}
}
}
}
}
int main(){
while(~scanf("%d",&n)){
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&a[i][j]);
spfa(1);
// for(int i=1;i<=n;i++) cout<<d[i]<<' ';
// putchar('\n');
s1=d[n]; s2=d[1];
spfa(n);
s2+=d[n];
// for(int i=1;i<=n;i++) cout<<d[i]<<' ';
// putchar('\n');
// cout<<s1<<' '<<s2<<endl;
printf("%d\n",min(s1,s2));
}
}
/*
4
1 2 4 10
2 0 1 1
2 2 0 5
6 3 1 2
3
*/
这里用了两遍spfa(当然也可以用djkstra两遍),事实上因为矩阵关系,所以边都知道,且点1到点1的最短路和点n到点n的最短路都只两个点(本身和其他n-1个点),于是可以遍历矩阵得到c1,c2.
for(int i=2;i<=n;i++) c1=min(c1,a[i][1]+a[1][i]);
for(int i=1;i<n;i++) c2=min(c2,a[i][n]+a[n][i]);
这样每次只要一次最短路运算。