0 or 1 HDU - 4370
题意
给定一个(n)阶矩阵(C_{ij}),找到满足以下条件的仅由0和1构成的(n)阶矩阵(X_{ij}):
-
(X_{12}+X_{13}+...+X_{1n}=1)
-
(X_{1n}+X_{2n}+...+X_{n-1n}=1)
-
对于(i)((1<i<n)),满足(sum{X_{ki}}(1≤k≤n)=sum{X_{ij}}(1≤j≤n))
求(sum{C_{ij} imes X_{ij}}(1≤i,j≤n))的最小值。
思路
-
首先想到,矩阵是图的一种表示方法。将给出的矩阵(C_{ij})视为一个图,则第(i)行第(j)列((1≤i,j≤n))的值(k),意思就是一条连接节点(i)与节点(j)的权值为(k)的边。
当矩阵仅由0,1构成时,即表示连接节点(i)与节点(j)的边是否存在。
-
由此再看矩阵(X_{ij}).
条件一:(X_{12}+X_{13}+...+X_{1n}=1)
即矩阵第1行的所有值中仅有1个1,这就表示节点1的出度为1。注意式中不包括(X_{11}),说明没有 节点(1)→节点(1) 的不经过其他节点的自环。
条件二:(X_{1n}+X_{2n}+...+X_{n-1n}=1)
即矩阵第(n)列的所有值中仅有1个1,这就表示节点n的入度为(1)。注意式中不包括(X_{nn}),说明没有 节点(n)→节点(n) 的不经过其他节点的自环。
条件三:对于(i)((1<i<n)),(sum{X_{ki}}(1≤k≤n)=sum{X_{ij}}(1≤j≤n))
即矩阵第(i)行之和等于矩阵第(i)列之和,也就是说除了节点1和节点n以外,其他节点的出入度相等。
显然,矩阵(X_{ij})表示的情况有以下两种:如果节点1与节点n是两个不同的节点,则为一条从节点1到节点n的路径;如果节点1与节点n是同一个节点,则为一个从该节点出发,经过一个及以上其他节点后再回到该节点的环(不能为零个,原因在上文已说明)。
-
综上,({C_{ij} imes X_{ij}}(1≤i,j≤n))即在图C上一条从节点1到节点n的路径,或图C上节点1的环+节点n的环。
对此求和即对路径上所有边的权值求和,并且求的是和的最小值,所以问题可转化为:求图C上节点1到节点n的最短路、节点1的环+节点n的环,这两条路径中权值和最小的值。
理清题意后就是最短路+最小环问题了,SPFA算法变形如下。
inline int read() {
int s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch>'9') { if (ch == '-')w = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
int n;
int C[maxn][maxn];
int d[maxn];
bool inq[maxn];
void spfa(int s) {
memset(inq, false, sizeof(inq));
queue<int> q;
for (int i = 1; i <= n; i++) {
if (i == s) d[i] = INF;
//在普通SPFA中,一般设d[s]=0,这可以看作是s的边权为0的自环
//当这里设d[s]=INF,则可以看作是自环边权无穷大,即不存在自环
//而且这里起点不入队
else {
d[i] = C[s][i];
//d的初始值不为INF,而为与起点的距离
q.push(i);
//将与起点连接的节点入队
inq[i] = true;
}
}
while (!q.empty()) {
int u = q.front(); q.pop();
inq[u] = false;
for (int i = 1; i <= n; i++) {
if (d[u] + C[u][i] < d[i]) {
d[i] = d[u] + C[u][i];
if (!inq[i]) {
q.push(i);
inq[i] = true;
}
}
}
}
}
int main()
{
// ios::sync_with_stdio(false);
/// int t; cin >> t; while (t--) {
while (cin >> n) {
memset(d, 0, sizeof(d));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
C[i][j] = read();
}
}
spfa(1);
int path = d[n];
int cir_1 = d[1];
spfa(n);
int cir_n = d[n];
int ans = min(path, cir_1 + cir_n);
cout << ans << endl;
}
return 0;
}