【二分图最佳匹配】移动棋子

来源:互联网 发布:国内旅游2017年大数据 编辑:程序博客网 时间:2024/05/19 19:44

在一个n*n的棋盘上有n枚棋子。每次可以把一枚棋子往上、下、左、右方向之一移动一格,最后排成一行、一列或者主、副对角线上(因此一共有2n+2条可能的目标状态),要求移动次数最小。棋盘上有一些位置是障碍,棋子在任何时候都不能经过。棋子的初始位置保证不在障碍物上。任两枚棋子不能在同时到达同一个格子。


【输入文件】    输入文件move.in第一行包含两个整数n, m,表示棋子的个数(它也是棋盘的边长)和障碍的个数。以下n行,每行两个整数(x, y),表示第i个棋子的坐标(1<=x, y<=n),以下m行,每行给出一个障碍物的坐标。假设这n+m个坐标两两不重合。【输出文件】    输出文件仅包含一个整数,表示最小移动步数。如果无解,输出-1。【样例】move.in5 11 22 43 45 15 31 1move.out6【限制】50%的数据满足:2<=n<=15,m=0100%的数据满足:2<=n<=50, 0<=m<=100

此题考察KM算法的应用。首先以所有棋子为起点,分别求一次单源最短路(由于图非常稀疏,所以用广搜接就行了。然后,枚举终点(按行,列,以及两条对角线枚举),分别做一次最小匹配(用KM算法),最后在所有的匹配值中取一个最小值即可。Accode:

#include <cstdio>#include <cstdlib>#include <algorithm>#include <string>#include <cstring>const char fi[] = "move.in", fo[] = "move.out";const int maxN = 60, SIZE = 0xffff;const int MAX = 0x3f3f3f3f, MIN = ~MAX;const int dx[] = {0, 0, 1, -1};const int dy[] = {1, -1, 0, 0};struct Node{    int x, y; Node() {}    Node(int x, int y): x(x), y(y) {}} q[SIZE + 1], chess[maxN], target[maxN];bool _chess[maxN], _target[maxN];bool mp[maxN][maxN], vis[maxN][maxN];int lx[maxN], ly[maxN], dist[maxN][maxN][maxN];int Link[maxN], n, m, f, r, ans = MAX;inline int getint(){    int res = 0; char tmp;    while (!isdigit(tmp = getchar()));    do res = (res << 3) + (res << 1) + tmp - '0';    while (isdigit(tmp = getchar()));    return res;}void init(){    freopen(fi, "r", stdin);    freopen(fo, "w", stdout);    n = getint(); m = getint();    for (int i = 0; i < n; ++i)    {        int x = getint(), y = getint();        chess[i] = Node(x - 1, y - 1);    }    memset(mp, 1, sizeof mp);    for (int i = 0; i < m; ++i)    {        int u = getint(), v = getint();        mp[u - 1][v - 1] = 0;    }    return;}void Bfs(int S){    memset(vis, 0, sizeof vis);    dist[S][chess[S].x][chess[S].y] = 0;    vis[chess[S].x][chess[S].y] = 1;    q[(r = f = 0)++] = chess[S];    while (f < r)    {        Node Now = q[f++];        int x = Now.x, y = Now.y;        for (int j = 0; j < 4; ++j)        {            int u = x + dx[j], v = y + dy[j];            if (u < 0 || u >= n || v < 0 || v >= n)                continue;            if (mp[u][v] && !vis[u][v])            {                vis[u][v] = 1;                dist[S][u][v] = dist[S][x][y] + 1;                q[r++] = Node(u, v);            }        }    }    return;}bool Find(int i){    _chess[i] = 1;    for (int j = 0; j < n; ++j)    if (!_target[j] && lx[i] + ly[j] ==        dist[i][target[j].x][target[j].y])    {        _target[j] = 1;        if (Link[j] == -1 || Find(Link[j]))        {Link[j] = i; return 1;}    }    return 0;}int KM(){    for (int i = 0; i < n; ++i)    {        lx[i] = MAX; ly[i] = 0; Link[i] = -1;        for (int j = 0; j < n; ++j)            lx[i] = std::min(lx[i],                dist[i][target[j].x][target[j].y]);    }    for (int k = 0; k < n; ++k)//这里循环变量一定不能用i。    while (1)    {        memset(_chess, 0, sizeof _chess);        memset(_target, 0, sizeof _target);        if (Find(k)) break; int Max = MIN;        for (int i = 0; i < n; ++i) if (_chess[i])        for (int j = 0; j < n; ++j) if (!_target[j])            Max = std::max(Max, lx[i] + ly[j] -                           dist[i][target[j].x]                           [target[j].y]);        for (int i = 0; i < n; ++i)        {            if (_chess[i]) lx[i] -= Max;            if (_target[i]) ly[i] += Max;        }    } //注意求最小匹配和最大匹配略有不同。    int ans = 0;    for (int i = 0; i < n; ++i)        ans += dist[Link[i]][target[i].x][target[i].y];    return ans;}void work(){    memset(dist, 0x3f, sizeof dist);    for (int i = 0; i < n; ++i) Bfs(i);//一定要以每个棋子作为起点求最短路,否则浪费。    for (int i = 0; i < n; ++i)    {        bool ok = 1;        for (int j = 0; j < n; ++j)            if (!mp[i][j]) {ok = 0; break;}        if (!ok) continue;        for (int j = 0; j < n; ++j)            target[j] = Node(i, j);        ans = std::min(ans, KM());    }    for (int j = 0; j < n; ++j)    {        bool ok = 1;        for (int i = 0; i < n; ++i)            if (!mp[i][j]) {ok = 0; break;}        if (!ok) continue;        for (int i = 0; i < n; ++i)            target[i] = Node(i, j);        ans = std::min(ans, KM());    }    bool ok = 1;    for (int i = 0; i < n; ++i)        if (!mp[i][i]) {ok = 0; break;}    if (ok)    {        for (int i = 0; i < n; ++i)            target[i] = Node(i, i);        ans = std::min(ans, KM());    }    ok = 1;    for (int i = 0; i < n; ++i)        if (!mp[i][n - i - 1]) {ok = 0; break;}    if (ok)    {        for (int i = 0; i < n; ++i)            target[i] = Node(i, n - i - 1);        ans = std::min(ans, KM());    }    printf("%d\n", ans < MAX ? ans : -1);    return;}int main(){    init();    work();    return 0;}