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的时间是一样的.
- poj 2195
- poj 2195
- poj-2195
- poj 2195
- poj 2195
- poj 2195
- POJ 2195 Going Home
- POJ 2195 Going Home
- POJ-2195-Going Home
- poj 2195 java
- poj 2195 Going Home
- POJ 2195 min_cost_max_flow
- POJ 2195 Going Home
- poj 2195 Going Home
- POJ 2195 Going Home
- poj 2195 Going Home
- POJ 2195 Going Home
- POJ 2195 Going Home
- [C\C++]何谓封装
- C语言的第二天
- 浅出C++封装性
- web前端面试题
- 代理服务器Squid 使用详解
- poj-2195
- Git使用操作指南和GitHub
- 重写strcpy,strcat,strcmp函数
- 更新Android SDK Manager Mac下修改hosts
- hdu 1907(NIM 变形)
- POJ 1426 Find The Multiple
- cocos2dx函数生命周期
- solaris更改ssh,telnet
- iOS---UITextField动态判断汉字个数