HDU - 4370 0 or 1 (最短路)(思维转换)(SPFA)
Problem Description
Given a n*n matrix Cij (1<=i,j<=n),We want to find a n*n matrix Xij (1<=i,j<=n),which is 0 or 1.
Besides,Xij meets the following conditions:
1.X12 + X13 + ...X1n = 1
2.X1n + X2n +... Xn-1 n = 1
3.for each i (1<i<n), satisfies ∑Xki (1<=k<=n) = ∑Xij (1<=j<=n).
For example, if n=4,we can get the following equality:
X12 + X13 + X14 = 1
X14 + X24 + X34 = 1
X12 + X22 + X32 + X42 = X21 + X22 + X23 + X24
X13 + X23 + X33+ X43 = X31 + X32 + X33 + X34
Now ,we want to know the minimum of ∑Cij * Xij (1<=i,j<=n) you can get.
Hint
For sample, X12=X24=1,all other Xij is 0.
Input
The input consists of multiple test cases (less than 35 case).
For each test case ,the first line contains one integer n (1<n<=300).
The next n lines, for each lines, each of which contains n integers, illustrating the matrix C, The j-th integer on i-th line is Cij(0<=Cij<=100000).
Output
For each case, output the minimum of ∑Cij*Xij you can get.
Sample Input
4
1 2 4 10
2 0 1 1
2 2 0 5
6 3 1 2
Sample Output
3
题意
给你一个n*n的矩阵,要求你找到一个由0,1组成的n*n的矩阵,使得俩个矩阵对应元素乘积的和最小。
思路
此题可以转换成最短路来写(骚..................)
而求得的0,1矩阵乘以原矩阵,可以看作1为走这条路,0为不走。
对应点乘机的累计和可以看作求1到n的最短路,或点1的最小闭环和点n的最小闭环。为什么?
题意要求的0,1矩阵
1.X12 + X13 + ...X1n = 1 要求点1的出度为1,不存在点1指向1的情况
2.X1n + X2n +... Xn-1 n = 1 要求点n的入度为1,不存在点n指向n的情况
3.for each i (1<i<n), satisfies ∑Xki (1<=k<=n) = ∑Xij (1<=j<=n).除点1和点n外,其他点的出入度相等。即一定有1到n的路或者有1自环和n自环。
点1出度为一(条件一),那么一定要到另一个点,如果到n则求1到n的最短路,如果到其他点,由于其他点的出入度要求相同(条件三),
这时需要该点一定有一条出去的边,如果到n则有1到n的最短路,如果不到n点则必然有1-->1的闭环,到其他点(最终都是要到1,n的)。
当有1-->1的自环时,点n的入度为一,则必有一个点指向n,并且只能是点1,n以外的其他点,由于其他点的出入度要求相同(条件三),此时必有n-->n的闭环。
所以一定有1到n的路或 有n的自环和1的自环。
所以需要计算1到n的最短路,1自环和n自环的总值,两者最小就为答案
即计算一遍起点为1的d1[n],d1[1],再计算一遍起点为n的d2[n],输出 min(d1[n],d2[1]+d2[n]);
值得注意的是这里的最短路写法(spfa)有点不一样,
因为要算起点自环的花费,所以初始化dist[strat]=inf,不能为dist[s]=0,
这样第一遍更新找其他点到起点的距离d就更新不了,
那么可以这样:自己更新第一次,手动将所有的点都加入队列即可。
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<queue>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
int n;
int dist[310],vis[310];
int mapp[310][310];
void Spfa(int s)
{
queue<int> q;
memset(vis,0,sizeof(vis));
//memset(dist,INF,sizeof(vis));
for(int i=1;i<=n;i++)
{
if(i==s)//因为求闭环,源点设置为INF
dist[i]=INF;
else
{
dist[i]=mapp[s][i];
q.push(i);
vis[i]=1;
}
}
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=1;i<=n;i++)
{
if(dist[i]>dist[u]+mapp[u][i])
{
dist[i]=dist[u]+mapp[u][i];
if(!vis[i])
q.push(i),vis[i]=1;
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
while(cin>>n)
{
int path,dist1,dist2;
//path记录1到n的距离,dist2记录点1最小闭环的大小,dist2记录点n最小闭环的大小
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
cin>>mapp[i][j];
Spfa(1) ,path=dist[n], dist1=dist[1];
Spfa(n), dist2=dist[n];
cout<<min(path,dist1+dist2)<<endl;//最短路距离是1到n,和点1闭环+点2闭环中最小的一个
}
return 0;
}