输出一个整数,为矿工获得所有宝藏的最小代价。
2 2 2
10 9
10 10
10 1
10 10
1 1 1
1 2 2
据说h=1时与WC 2008 的 游览计划完全相同。
先看看h=1时怎么做。
设f[x][y][s]表示到(x,y)这个点经过了s这个集合的宝藏的最小代价。
f[x][y][s] = min{
1. f[x][y][s′]+f[x][y][s/s′](s′∈s)
2. f[x][y−1][s]+a[x][y]
3. f[x][y+1][s]+a[x][y]
4. f[x−1][y][s]+a[x][y]
5. f[x+1][y][s]+a[x][y]
}
我们惊奇地发现2-5方程有后效性,怎么办?
不要方,我们把它YY成一个有向图,然后你发现SPFA展现神威了。
从小到大枚举S,先把第一条方程给维护了,每个点一开始的f[x][y][s]就确定了,但是不一定是最短的,接着把每个点都加进队列,跑SPFA求最小值。
但是,有人可能会有疑问,万一我们已经走过了(x,y),又走了过来,不就重复算了a[x][y]吗?是的,我们一定会遇到这种情况,把这想象成一个无向的图,我们要跑的是最短路,重复走相当于走过这条边再绕回来,你在最短路中会这么走吗?
那么h=1的情况就解决了。
那如果有多层呢?
在求完每一层后,对于每个点(x,y),从它这儿下去的代价就是f[x][y][Full],对于从上一层传下来的,我们可以给它再开一个宝藏位,表示是否携带了上一层传下来的代价,因为二者只用取其一,这和宝藏是一个道理。
预处理,就把所有已知的扔进去,就可以不管了,因为SPFA功能强大。
Code:
#include
#include
#include
#include
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
#define min(a,b) ((a) < (b) ? (a) : (b))
using namespace std;
int move[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
int h,n,m,a[11][11][11], K[11];
struct node {
int x, y;
}b[11][11];
int o, f[2][12][12][1025];
int a2[12], d[10][1025], d0[12], s1[1025];
int t[5000000][2], bz[12][12], bx[1025];
void Init() {
scanf("%d %d %d", &h, &n, &m);
fo(k, 1, h) fo(i, 1, n) fo(j, 1, m) scanf("%d", &a[k][i][j]);
fo(k, 1, h) {
scanf("%d", &K[k]);
fo(i, 1, K[k])
scanf("%d %d", &b[k][i].x, &b[k][i].y);
}
a2[0] = 1; fo(i, 1, 11) a2[i] = a2[i - 1] << 1;
d0[0] = 1; d[0][1] = 0; s1[0] = 0;
fo(i, 1, 1023) {
fo(j, 0, 9) if(i & a2[j]) s1[i] ++;
d[s1[i]][++ d0[s1[i]]] = i;
}
}
void Spfa() {
fd(c, h, 1) {
o = !o;
memset(f[o], 60, sizeof(f[o]));
fo(x, 1, n)
fo(y, 1, m) {
f[o][x][y][0] = a[c][x][y];
f[o][x][y][1] = f[!o][x][y][a2[K[c + 1] + 1] - 1] + a[c][x][y];
}
fo(i, 1, K[c]) {
int x = b[c][i].x, y= b[c][i].y;
f[o][x][y][a2[i]] = a[c][x][y];
f[o][x][y][a2[i] + 1] = f[!o][x][y][a2[K[c + 1] + 1] - 1] + a[c][x][y];
}
memset(bx, 0, sizeof(bx));
fo(g, 0, K[c] + 1) {
fo(gg, 1, d0[g]) {
int nowc = d[g][gg];
if(nowc > a2[K[c] + 1] - 1) continue;
bx[nowc] = 1;
fo(othc, 0, 1023) if((othc & nowc) == othc) {
int newc = othc ^ nowc;
fo(x, 1, n) fo(y, 1, m)
f[o][x][y][nowc] = min(f[o][x][y][nowc], f[o][x][y][newc] + f[o][x][y][othc] - a[c][x][y]);
}
int st = 0, en = 0;
fo(x, 1, n) fo(y, 1, m) t[++ en][0] = x, t[en][1] = y, bz[x][y] = 1;
while(st < en) {
int x = t[++st][0], y = t[st][1];
fo(k, 0, 3) {
int l = x + move[k][0], r = y + move[k][1];
if(l > 0 && l <= n && r > 0 && r <= m) {
if(f[o][x][y][nowc] + a[c][l][r] < f[o][l][r][nowc]) {
f[o][l][r][nowc] = f[o][x][y][nowc] + a[c][l][r];
if(!bz[l][r])
bz[l][r] = 1, t[++en][0] = l, t[en][1] = r;
}
}
}
bz[x][y] = 0;
}
}
}
}
}
int main() {
freopen("treasure.in", "r", stdin);
freopen("treasure.out", "w", stdout);
Init();
Spfa();
int ans = 1e9;
fo(x, 1, n) fo(y, 1, m) ans = min(ans , f[o][x][y][a2[K[1] + 1] - 1]);
printf("%d",ans);
}