【最短路/矩阵+最小环】0 or 1 HDU - 4370
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}\times 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}\times 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;
}