原题: http://acm.hdu.edu.cn/showproblem.php?pid=3468
题意:
n* m的地图,上面有‘#’(不能走),‘.’(可以走),‘*’(宝藏)和字母。有两个人小A和小B,两人都会从地图A出发,小A会从A开始按顺序走到B,C…Z,a,b…z,一直走到最后一个单词为止,即如果只有ABC就走到C,如果只有ABD那么就不合法,每次都走最短路。小B也会按小A的速度走最短路(即同时到达下一个点),但是他可以走不同的路线,如果路线上有宝藏,他就会挖宝藏,但是从一个字母走到另一个字母的过程中他只能挖一个宝藏。
如果过程不合法请输出-1,如果合法,请输出小B最多可以获得多少个宝藏。
解析:
先排-1的情况有:
- 只有ABDE
- 出现AABC
- A走不到B
做一遍最短路。对于一个宝藏点p,第i个目标点 ,如果 ,则说明可以在 到 的途中拿到。
建图:
- 将 到 这条路看成一个点 ;宝藏点p因为只能被采一次,所以裂点
#include<bits/stdc++.h>
using namespace std;
#define pill pair<int,int>
#define LL long long
const int inf = 0x3f3f3f3f;
const int N = 20500, M = 205000;
int head[N], nex[M], to[M], val[M], now;
void add(int a, int b, int v) {
to[++now] = b;
val[now] = v;
nex[now] = head[a];
head[a] = now;
to[++now] = a;
val[now] = 0;
nex[now] = head[b];
head[b] = now;
}
//*********************
int sp, ep, d[N];
int bfs() {
queue<int>Q;
memset(d, -1, sizeof(d));
d[sp] = 0;
Q.push(sp);
while(!Q.empty()) {
int p = Q.front();
Q.pop();
for(int i = head[p]; ~i; i = nex[i]) {
int u = to[i];
if(d[u] == -1 && val[i] > 0) {
d[u] = d[p] + 1;
Q.push(u);
}
}
}
return d[ep] != -1;
}
int dfs(int p, int v) {
int r = 0;
if(p == ep)
return v;
for(int i = head[p]; (~i) && r < v; i = nex[i]) {
int u = to[i];
if(val[i] > 0 && d[u] == d[p] + 1) {
int x = dfs(u, min(val[i], v - r));
r += x;
val[i] -= x;
val[i ^ 1] += x;
}
}
if(!r)
d[p] = -2;
return r;
}
LL dinic() {
LL ans = 0, t;
while(bfs()) {
while(t = (LL)dfs(sp, inf))
ans += t;
}
return ans;
}
//***********************
void init() {
now = -1; //要求第一条边为0
memset(head, -1, sizeof(head));
}
const int di[4][2] = {{1, 0}, {0, 1}, {0, -1}, {-1, 0}};
char Mp[101][101];
int n, m;
int id(int x, int y) {
return (x - 1) * m + y - 1;
}
pill pos[60];
int up;
int dis[60][10100];
bool vis[10100];
void get_dis() {
for(int i = 1; i <= up; i++) {
for(int j = 0; j <= id(n, m); j++) {
dis[i][j] = inf;
}
}
for(int i = 1; i <= up; i++) {
memset(vis, 0, sizeof(vis));
int x = pos[i].first, y = pos[i].second;
queue<int> Q;
dis[i][id(x, y)] = 0;
Q.push(id(x, y));
vis[id(x, y)] = 1;
while(!Q.empty()) {
x = Q.front() / m + 1, y = Q.front() % m + 1;
Q.pop();
for(int j = 0; j < 4; j++) {
int xx = x + di[j][0], yy = y + di[j][1], Id = id(xx, yy);
if(xx < 1 || yy < 1 || xx > n || yy > m || vis[Id] || Mp[xx][yy] == '#')
continue;
if(dis[i][Id] > dis[i][id(x, y)] + 1) {
dis[i][Id] = dis[i][id(x, y)] + 1;
Q.push(Id);
vis[Id] = 1;
}
}
}
}
}
int Build() {
for(int i = 1; i < up; i++) {
int Id = id(pos[i].first, pos[i].second);
add(sp, Id, 1);
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(Mp[i][j] != '*')
continue;
int Id = id(i, j);
add(Id, Id + id(n, m) + 1, 1);
add(Id + id(n, m) + 1, ep, 1);
for(int k = 1; k < up; k++) {
int id_k = id(pos[k].first, pos[k].second);
int id_k1 = id(pos[k + 1].first, pos[k + 1].second);
if(dis[k][id_k1] == inf)
return -1;
if(dis[k][Id] + dis[k + 1][Id] == dis[k][id_k1])
add(id_k, Id, 1);
}
}
}
}
int main() {
while(cin >> n >> m) {
sp = N - 2, ep = N - 1;
init();
for(int i = 1; i <= n; i++)
scanf("%s", Mp[i] + 1);
up = 0;
int co = 0;
memset(pos, 0, sizeof(pos));
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(Mp[i][j] == '*' || Mp[i][j] == '.' || Mp[i][j] == '#')
continue;
co++;
if(Mp[i][j] <= 'Z')
pos[Mp[i][j] - 'A' + 1] = {i, j}, up = max(up, Mp[i][j] - 'A' + 1);
else
pos[Mp[i][j] - 'a' + 27] = {i, j}, up = max(up, Mp[i][j] - 'a' + 27);
Mp[i][j] = '.';
}
}
if(co != up) {
printf("-1\n");
continue;
}
bool f = 1;
for(int i = 1; i <= up; i++) {
if(pos[i].first == 0)
f = 0;
}
if(!f) {
printf("-1\n");
continue;
}
get_dis();
if(Build() == -1) {
printf("-1\n");
continue;
}
int ans = dinic();
printf("%d\n", ans);
}
}