zoukankan      html  css  js  c++  java
  • 【最短路/矩阵+最小环】0 or 1 HDU

    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;
    }
    
  • 相关阅读:
    javascript 之迭代器
    前端数据结构--二叉树先序、中序、后序 递归、非递归遍历
    前端数据结构--树
    前端数据结构--散列表(哈希表)
    前端数据结构--线性结构-队列、栈
    前端数据结构--线性结构-链表
    前端数据结构--线性结构-数组
    前端数据结构---复杂度分析
    前端数据结构---相关基础概念
    css整理之-----------基本知识
  • 原文地址:https://www.cnblogs.com/streamazure/p/13399696.html
Copyright © 2011-2022 走看看