【最短路/矩阵+最小环】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;
}
posted @ 2020-07-29 21:19  StreamAzure  阅读(123)  评论(0编辑  收藏  举报