POJ 1734

题意:一个无向图,所有边权为正,求这个无向图中的一个权值最小的环(环的权值定义为环中所有边权值的和)。要求环的顶点数至少为3个,且图中可能存在重边。

分析:这个题我最开始只有搜索的想法,后来看到了floyd的做法,才折服了,向强大的floyd致敬! 这个题可以这么想,抛开floyd的思路,我们知道每个环中都有一个顶点标号最大的顶点(比如为k)。我们可以枚举这个顶点,枚举完这个顶点之后我们再枚举另外两个和它相邻的顶点i和j(i,j < k)。如果这个时候我们知道i到j的不经过k的最短路(设为p),那么我们就可以把边i->k,k->j和路径p组合起来,成为一个环,如果这个环的权值小于当前求出的所有环里面的最小权值的话,那么就更新一下最小值,并记录路径。

恩,这个思路看上去是挺好的,但是我们怎么求i到j的不经过k的最短路呢?想到了什么?floyd! floyd的本质是个dp,dp[k][i][j]代表i到j的中间节点(不包括i和j)都在区间[1,k]时,i到j的最短路。floyd算法的最外层循环是个从小到大枚举k的过程,当最外层刚刚进入第k次循环的时候,我们已经得到了所有点对的dp[k-1][][]的值,也就是所有点对(i,j)的i到j的中间节点都在[1,k-1]区间的i到j的最短路,如果在这个时候我们顺便设定一下所求环中的顶点编号最大的也为k,那么我们可以根据dp[k-1][][]的值求出一个环,做法就是枚举k的相邻节点i和j(i,j < k)。这个时候dp[k-1][i][j]记录的最短路一定不包含点k,那么i->k,k->j和dp[k-1][i][j]就构成了一个环。

/*
    POJ 1734
    code by heifrank
*/
#include <iostream>
using namespace std;

const int MV = 105;
const int INF = 1<<30;

int G0[MV][MV], G[MV][MV], pre[MV][MV];
int path[MV];
int cnt;

void update(int a, int b, int k){
    cnt = 0;
    int p = b;
    do{
        path[cnt++] = p;
        p = pre[a][p];
    }while(p != a);
    path[cnt++] = a, path[cnt++] = k;
}

int main(){
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i=1; i<=n; i++){
        for(int j=1; j<=n; j++)
            G[i][j] = INF, pre[i][j] = i;
        G[i][i] = pre[i][i] = 0;
    }

    int u, v, w;
    while(m--){
        scanf("%d%d%d", &u, &v, &w);    
        if(G[u][v] > w)G[u][v] = G[v][u] = w;
    }

    for(int i=1; i<=n; i++)for(int j=1; j<=n; j++)G0[i][j] = G[i][j];
    int ans = INF;

    for(int k=1; k<=n; k++){
        for(int i=1; i<k; i++)for(int j=i+1; j<k; j++)
            if(G0[i][k] != INF && G0[k][j] != INF){
                int tv = G[i][j] + G0[i][k] + G0[k][j];
                if(tv < ans){
                    ans = tv;
                    update(i, j, k);
                }   
            }

        for(int i=1; i<=n; i++)for(int j=1; j<=n; j++)
            if(G[i][k] != INF && G[k][j] != INF){
                int tv = G[i][k]+G[k][j];
                if(tv < G[i][j])G[i][j] = tv, pre[i][j] = pre[k][j];
            }   
    }
    if(ans == INF)puts("No solution.");
    else{
        for(int i=0; i<cnt; i++)
            printf("%d%c", path[i], i==cnt-1?'\n':' '); 
    }

    return 0;   
}

Comments

Pages

Categories

Tags