hdu3468 Treasure Hunting(二分图)

在一个n*m的格子里面,有一些宝藏和一些聚集地;

‘.’ :表示空地,可以通过;
‘*’ :表示宝藏,可以通过;
‘#’ :表示墙,不能通过;
‘A’ ~ ‘Z’ || ‘a’ ~ ‘z’ :表示聚集地;

聚集地具有等级,就是他们的字典序;从最低字典序聚集地进入,从最高字典序聚集地出。且每次只能从一个聚集地出发去偷宝藏后要到其字典序后一个聚集地去,例如:A->x1->B->x2->C……同时是走最短路径。问最多能偷几个宝藏?
思路:

算出两个字典序之间的最短之间存不存在宝藏,有就连边,没就算了。
然后判断从最小字典序可不可以按顺序走到最高字典序;
对每个宝藏进行编号,如果某宝藏在两字典序的最短路之间;
k->x->k+1,那么就连接 <k,x> <script type="math/tex" id="MathJax-Element-8"> </script>;
对构成的图进行二分图匹配,最大匹配就是结果;

int dis[70][10010];
int d[70];
int mp[70][10010];
char s[101][101];
int n, m;
int ID(char c) {
    if (c >= 'A' && c <= 'Z') return c - 'A';
    return c - 'a' + 26;
}
const int dx[] = {-1, 0, 1, 0};
const int dy[] = {0, -1, 0, 1};
void bfs(int x,int y,int id) {
    queue<int> que;
    que.push(x), que.push(y);
    dis[id][x*m + y] = 0;
    while(!que.empty()) {
        x = que.front();que.pop();
        y = que.front();que.pop();
        for (int i = 0;i < 4;++i) {
            int nx = x + dx[i];
            int ny = y + dy[i];
            if (nx >= 0 && nx < n && ny >= 0 && ny < m && s[nx][ny] != '#' && dis[id][nx * m + ny] == -1) {
                dis[id][nx*m + ny] = dis[id][x*m + y] + 1;
                que.push(nx);que.push(ny);
                // cout << s[nx][ny] << ' ';
                if (isalpha(s[nx][ny]) && ID(s[nx][ny]) + 1 == id) {
                    d[ID(s[nx][ny])] = dis[id][x*m + y] + 1;
                    // bug;
                }
            }
        }
    }
    // cout << endl;
}
int linker[10010];
bool vis[10010];
bool dfs(int u,int& p) {
    for (int i = 0;i < p;++i) {
        if (!vis[i] && mp[u][i]) {
            vis[i] = true;
            if (linker[i] == -1 || dfs(linker[i], p)) {
                linker[i] = u;
                return true;
            }
        }
    }
    return false;
}
int hungary(int& top, int& p) {
    int ret = 0;
    memset(linker, -1,sizeof linker);
    for (int i = 0;i < top;++i) {
        memset(vis, false,sizeof vis);
        if (dfs(i, p)) ret++;
    }
    return ret;
}

int main(int argc, const char * argv[])
{    
    // freopen("in.txt","r",stdin);
    // freopen("out.txt","w",stdout);
    // clock_t _ = clock();

    while(~scanf("%d%d", &n, &m)) {
        for (int i = 0;i < n;++i)
            scanf("%s", s[i]);
        int top = 0;
        memset(dis, -1,sizeof dis);
        memset(d, -1,sizeof d);
        for (int i = 0;i < n;++i) {
            for (int j = 0;j < m;++j) {
                if (isalpha(s[i][j])) {
                    top = max(top, ID(s[i][j]));
                    bfs(i, j, ID(s[i][j]));
                }
            }
        }
        bool ok = true;
        // for (int i = 0;i < top;++i)
        //     cout << d[i] << ' ';
        // cout << endl;
        for (int i = 0;i < top;++i) {
            if (d[i] == -1) {ok = false;break;}
        }
        if (!ok) {puts("-1");continue;}
        int p = 0;
        memset(mp, 0,sizeof mp);
        for (int i = 0;i < n;++i) {
            for (int j = 0;j < m;++j) {
                if (s[i][j] == '*') {
                    for (int k = 0;k < top;++k) {
                        if (dis[k][i*m+j] + dis[k+1][i*m+j] == d[k])
                            mp[k][p] = 1;
                    }
                    p++;
                }
            }
        }
        cout << hungary(top, p) << endl;
    }

    // printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值