poj-2195

来源:互联网 发布:微信群里发淘宝优惠券 编辑:程序博客网 时间:2024/05/22 15:04
<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 110int map[MAX][MAX]; // 0: space, even: human odd: houseint N; // number of rows, heightint M; // number of columns. widthstruct 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 housechar 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 1char 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 999999int 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 macthchar 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的时间是一样的.

0 0
原创粉丝点击