poj-2195

17 篇文章 0 订阅
11 篇文章 0 订阅
<span style="background-color: rgb(255, 255, 255); font-family: Arial, Helvetica, sans-serif;"></span><pre name="code" class="html">//664K  16MS    G++
// 696K    16MS    G++ with slack
#include <cstdio>
#include <cstring>
#include <queue>

using namespace std;

#define MAX 110

int map[MAX][MAX]; // 0: space, even: human odd: house

int N; // number of rows, height
int M; // number of columns. width

struct Point {
    int x;
    int y;
};

typedef struct Point Human;
typedef struct Point House;

Human humanInfo[MAX];
House houseInfo[MAX];

int houseDistance[MAX][MAX]; // record the min distance from each man to each house


char BFSFlag[MAX][MAX];

struct BFSNode {
    int x;
    int y;
    int distance;
};

queue<BFSNode> BFSQueue;


int humanNum;
int houseNum;

#define HUMAN 'm'
#define HOUSE 'H'

// if node is a house, return 1
char pushBFSNode(int newX, int newY, int newDistance, int curNodeId) {
    BFSFlag[newX][newY] = 1;
    BFSNode newNode;
    newNode.x = newX;
    newNode.y = newY;
    newNode.distance = newDistance + 1;
    BFSQueue.push(newNode);
    if (map[newX][newY] > 0 && map[newX][newY]%2 == 1) { // if it is a house
        int houseId = map[newX][newY]/2 +1;
        houseDistance[curNodeId][houseId] = -(newDistance + 1); // get min, so add minus
        return 1;
    }
    return 0;
}

void getSomeoneDistance(int beginId) {
    while(BFSQueue.size()) {
        BFSQueue.pop();
    }

    int reachedHouseNum = 0;

    memset(BFSFlag, 0, sizeof(BFSFlag));

    int beginX = humanInfo[beginId].x;
    int beginY = humanInfo[beginId].y;
    BFSNode beginNode;
    beginNode.x = beginX;
    beginNode.y = beginY;
    beginNode.distance = 0;
    BFSFlag[beginX][beginY] = 1;

    BFSQueue.push(beginNode);

    while(BFSQueue.size()) {
        BFSNode curNode = BFSQueue.front();
        BFSQueue.pop();
        int curX = curNode.x;
        int curY = curNode.y;
        int curDistance = curNode.distance;

        //up
        if (curY < N -1 && !BFSFlag[curX][curY+1]) {
            reachedHouseNum += pushBFSNode(curX, curY+1, curDistance, beginId);
        }
        //down
        if (curY > 0 && !BFSFlag[curX][curY-1]) {
            reachedHouseNum += pushBFSNode(curX, curY-1, curDistance, beginId);
        }
        //left
        if (curX > 0 && !BFSFlag[curX-1][curY]) {
            reachedHouseNum += pushBFSNode(curX-1, curY, curDistance, beginId);
        }
        //right
        if (curX < M -1 && !BFSFlag[curX+1][curY]) {
            reachedHouseNum += pushBFSNode(curX+1, curY, curDistance, beginId);
        }
        if (reachedHouseNum >= houseNum) {
            return;
        }
    }

}

#define INF 999999
int lx[MAX];
int ly[MAX];
int slack[MAX];

int V1[MAX];
int V2[MAX];
int V1Status[MAX];
int V2Status[MAX]; // 0: V1 V2 ethier no in macth 1: V1 in match, V2 not in match 2: V1 V2 both in the macth
char waittingMove[MAX];

char getPair(int curId) {
    // printf("getPair %d\n", curId);
    V1Status[curId] = 1; // curId is in alternating path
    for (int i = 1; i <= houseNum; i++) {// check all house
        int d = lx[curId] + ly[i] - houseDistance[curId][i];
        slack[i] = slack[i] < d ? slack[i]: d;
        if (lx[curId] + ly[i] == houseDistance[curId][i]) { // if satisfy the equal
            V2Status[i] = 1; // i is in alternating path
            if (!V2[i]) { // if i is not assigned yet
                V1[curId] = i;
                V2[i] = curId;
                return 1; // find best paired
            } else {
                if (!waittingMove[V2[i]]) {
                    waittingMove[V2[i]] = 1; // try to move i's owner
                    
                    if (getPair(V2[i])) { // if move success
                        V1[curId] = i;
                        V2[i] = curId;
                        return 1;
                    }
                }
            }
        }
    }
    return 0;
}

void adjustD() {
    int minD = INF;
    for (int i = 1; i <= houseNum; i++) {
        if (!V2Status[i]) { // if the Y not in match, but X in match
            minD = minD < slack[i] ? minD : slack[i];
        }
    }

    // for (int i = 1; i <= humanNum; i++) { // get minD, austerity method
    //     if(V1Status[i]) {
    //         for (int j = 1; j <= houseNum; j++) {
    //             if (!V2Status[j]) {
    //                 int tmp = lx[i] + ly[j] - houseDistance[i][j];
    //                 minD = minD < tmp ? minD : tmp;
    //             }
    //         }
    //     }
    // }

    for (int i = 1; i <= humanNum; i++) {
        if (V1Status[i]) {
            lx[i] -= minD;
        }
    }

    for (int i = 1; i <= houseNum; i++) {
        if (V2Status[i]) {
            ly[i] += minD;
        }
        if (slack[i] != INF) { // if not a INF, - minD
            slack[i] -= minD;
        }
    }


}

int getMaxW(int curNodeId) {
    int maxW = -INF;
    for (int i = 1; i <= humanNum; i++) {
        if (houseDistance[curNodeId][i] < 0) { // connected
            maxW = maxW > houseDistance[curNodeId][i] ? maxW : houseDistance[curNodeId][i];
        }
    }
    return maxW;
}

void KM() {

    for (int i = 1; i <= humanNum; i++) { // init lx
        lx[i] = getMaxW(i);
    }

    memset(ly, 0, sizeof(ly)); // init ly

    memset(V1, 0, sizeof(V1));
    memset(V2, 0, sizeof(V2));

    for (int i = 1; i <= humanNum; i++) {
        // printf("KM %d\n", i);
        for (int j = 1; j <= MAX; j++) { // init slack
                slack[j] = INF;
        }
        while (1) { // endless try until get i's best pair
            memset(waittingMove, 0, sizeof(waittingMove));
            memset(V1Status, 0, sizeof(V1Status));
            memset(V2Status, 0, sizeof(V2Status));
            if (getPair(i)) { // if find a best pair, quit
                break;
            }
            adjustD(); // if not get pair, adjust the lx and ly
        }
    }

    int sum = 0;
    for (int i = 1; i <= humanNum; i++) {
        sum += houseDistance[i][V1[i]];
    }
    printf("%d\n", -sum);
}

void getDistance() {
    for (int i = 1; i <= humanNum; i++) {
        getSomeoneDistance(i);
    }

    // for (int i = 1; i <= humanNum; i++) {
    //     for (int j = 1; j <= houseNum; j++) {
    //         printf("man: %d %d -> house: %d %d %d\n",
    //                 humanInfo[i].x, humanInfo[i].y, houseInfo[j].x, houseInfo[j].y
    //                 , houseDistance[i][j]);
    //     }
    // }
    KM();
}

void solve() {
    getDistance();
}

int main() {
    while(1) {
        scanf("%d %d", &N, &M);
        if (!N && !M) {
            return 0;
        }
        char row[MAX] = "";
        houseNum = 0;
        humanNum = 0;
        memset(map, 0, sizeof(map));
        memset(humanInfo, 0, sizeof(humanInfo));
        memset(houseInfo, 0, sizeof(houseInfo));
        memset(houseDistance, 0, sizeof(houseDistance));
        for (int i = 0; i < N; i++) {
            scanf("%s", row);
            for (int j = 0; j < M;j++ ) {
                if (row[j] == HUMAN) {
                    map[j][i] = (++humanNum)*2; // human Id begin from 2
                    humanInfo[humanNum].x = j;
                    humanInfo[humanNum].y = i;
                } else if (row[j] == HOUSE) {
                    map[j][i] = (houseNum++)*2 + 1; // house Id begin from 1;
                    houseInfo[houseNum].x = j;
                    houseInfo[houseNum].y = i;
                }
            }
        }
        solve();
    }
}
 
<span style="background-color: rgb(255, 255, 255); font-family: Arial, Helvetica, sans-serif;">KM算法小变形题,从权的和最大变成了权最小。</span>

本题难不在于转化,纯粹是难在转化,一开始要BFS求出每个man到每个house的最短距离,

然后再用KM算法求出最佳匹配。

KM算法第一次写,在匈牙利算法基础上增加些判断于flag即可,

首先,本题说明了man和house的数量相等,保证了最优匹配的存在,那么要求最小的权和,而KM能求的却是最大的权和,

这时候需要变形一下,因为保证了man到house的距离都是正数,因此可以将所有的权值变为对应的负数,然后求这些权值中的最优匹配即可(在权值全部是正数的情况下, 负数和最大 -> 正数和最小)。KM算法的流程:

两个点集V1(对应man) V2(对应house):

首先要设置两个顶标数组lx(for V1)和ly(for V2),初始值:

lx[i] 定义为V1的点 i 到V2的所有边的最大权值wMAX。

ly[i] 初始值均为0.

还需要一个slack数组来记录V2每个点对应的d,初始为INF

然后就是匈牙利算法的流程,对V1的每个点i求增广路得到在V2中的最佳匹配,

对每个点求最佳匹配前,要设置一些额外数组来保存信息:

V1status[] 来标示V1的某个点i是都在此次查找的增广路中,

V2status[] 来标示V2的某个点i是否在此次查找的增广路中,

每次调用getPair(i)来求最佳匹配时,先将V1status[i]置为1表示i在此次查找的增广路中。

然后每check一条边(i -> j),可以顺便的设置其slack[j]为 min(slack[j],  lx[i]+ly[j] - w(i,j)).

匹配的条件除了i与j之间有边以外,还要增加一条 即lx[i] + ly[j] = w(i, j), 满足此条件的j 也要设置V2status[j] = 1.

然后查看j是否已经被配过对了,如果没有,皆大欢喜,直接返回,为下一个V1的点配对,

如果j配过对了,那么按照匈牙利算法的规则,尝试 “挪” j原来的配对者到另外一个位置,即调用getpair(V2[j])。

如果最后对i点还找不到一个最优匹配,那么进行lx和ly的调整:

首先遍历所有V2status[x] == 0(即不在上面配对过程中所经过的增广路中)的V2的点的slack值,找到最小的slack作为D。

然后遍历lx[], 对所有V1status[x] == 1(在增广路中)的V1点i lx[i] -= D.

遍历所有的ly[] 对所有V2status[x] == 0 (不在增广路中)的V2点 ly[i] += D.

最后,还要对slack的每个成员 -=D。

然后再次开始对点i进行最优配对,如果还没有,继续调整,直到最后找到为止。

最后,在每个V1点的最优匹配都找到以后,累加最优匹配的每条边的权值取反即可。

本题数据弱,用slack优化 和 直接朴素(遍历每条边)求D的时间是一样的.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值