HDU - 4370 0 or 1(以矩阵的关系式表示最短路)

题目

给定一个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]);

这样每次只要一次最短路运算。

猜你喜欢

转载自blog.csdn.net/weixin_45606191/article/details/108938741