Description
Input
Output
输出一个整数,为矿工获得所有宝藏的最小代价。
Sample Input
2 2 2
10 9
10 10
10 1
10 10
1 1 1
1 2 2
Sample Output
30
Data Constraint
分析
h = 1 时,问题与 WC2008 的游览计划完全相同。用经典的连通性状 态压缩动态规划可以解决,但代码较繁琐。 注意到连通块是树状的,并且宝藏数 k 很小。可以设计另一种动态规划:设
f[x,y,S]
表示树根在点 (x,y),树包含的宝藏集合为 S 时,权值最小的树。状态转移方程为
f[x,y,S] = g[x,y,S] = min{f[x,y,S′] + f[x,y,S \S′]−a[x,y] | S′ ̸= Φ,S′ ⊂ S}
f[x−1,y,S] + a[x,y]
f[x + 1,y,S] + a[x,y]
f[x,y−1,S] + a[x,y] f[x,y + 1,S] + a[x,y]
转移方程具有后效性,但可以将问题视为最短路。由小到大枚举 S,先计 算
g[x,y,S]
,然后用 spfa 松弛。复杂度
O(nm3k)
。
在上述算法的外面套一个由底层往上递推的过程即可,计算每一层时, 要增加一个特殊的宝藏,为下面层的所有宝藏合并而成,即 f[i,x,y,1]=f[i+1,x,y,2K[i+1]+1−1] 。具体实现可以参考标程。复杂度为 O(hnm3k) 。
代码
#include <bits/stdc++.h>
#define H 15
#define N 15
#define M 15
#define K 1 << 9
#define INF 0x7ffffff
#define f first
#define s second
#define mp std::make_pair
typedef std::pair <int,std::pair<int,int> > MPD;
typedef std::pair <std::pair <int,int>,std::pair <int,int> > MPT;
const int dx[4] = {1,0,-1,0}, dy[4] = {0,1,0,-1};
int f[H][K][N][M][2];
int vis[N][M][2];
int a[H][N][M];
int num[N];
int sum[K];
int h,n,m;
int T;
int ans = INF;
int id = 0;
int now = 1;
int read()
{
char ch;
int x = 0,f = 1;
ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
bool cheak(int x,int y)
{
if (x && y && x <= n && y <= m)
return true;
return false;
}
int main()
{
freopen("treasure.in","r",stdin);
freopen("treasure.out","w",stdout);
h = read();
n = read();
m = read();
for (int i = 1; i <= h; i++)
for (int j = 1; j <= n; j++)
for (int k = 1; k <= m; k++)
a[i][j][k] = read();
memset(f,60,sizeof(f));
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
f[0][1][i][j][1] = 0;
for (int hn = 1; hn <= h; hn++)
{
T = read();
for (int i = 0; i < T; i++)
{
int x,y;
x = read();
y = read();
f[hn][1 << i][x][y][0] = a[hn][x][y];
f[hn][1 << i][x][y][1] = a[hn][x][y] + f[hn - 1][now][x][y][1];
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
f[hn][0][i][j][0] = a[hn][i][j], f[hn][0][i][j][1] = a[hn][i][j] + f[hn - 1][now][i][j][1];
for (int cf = 0; cf < (1 << T); cf++)
{
int cnt = 0;
for (int i = 0; i < T; i++)
if ((cf >> i) & 1)
num[cnt++] = i;
if (cnt)
{
for (int i = 1; i < (1 << cnt); i++)
{
sum[i] = 0;
for (int j = 0; j < cnt; j++)
{
if ((i >> j) & 1)
{
sum[i] |= (1 << num[j]);
}
}
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
for (int k = 0; k < (1 << cnt); k++)
{
f[hn][cf][i][j][0] = std::min(f[hn][cf][i][j][0], f[hn][sum[k]][i][j][0] - a[hn][i][j] + f[hn][cf ^ sum[k]][i][j][0]);
f[hn][cf][i][j][1] = std::min(f[hn][cf][i][j][1], f[hn][sum[k]][i][j][1] - a[hn][i][j] + f[hn][cf ^ sum[k]][i][j][0]);
}
}
std::priority_queue<MPT> Q;
++id;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
Q.push(mp(mp(-f[hn][cf][i][j][0],0),mp(i,j))),Q.push(mp(mp(-f[hn][cf][i][j][1],1),mp(i,j)));
while (!Q.empty())
{
MPT u = Q.top();
Q.pop();
int tox = u.s.f;
int toy = u.s.s;
int z = u.f.s;
if (vis[tox][toy][z] >= id)
continue;
vis[tox][toy][z] = id;
if (!z)
{
if (f[hn][cf][tox][toy][1] > f[hn][cf][tox][toy][0] + f[hn - 1][now][tox][toy][1])
f[hn][cf][tox][toy][1] = f[hn][cf][tox][toy][0] + f[hn - 1][now][tox][toy][1], Q.push(mp(mp(-f[hn][cf][tox][toy][1],1),mp(tox,toy)));
}
for (int k = 0; k <= 3; k++)
{
int nx = tox + dx[k];
int ny = toy + dy[k];
if (cheak(nx,ny) && f[hn][cf][nx][ny][z] > f[hn][cf][tox][toy][z] + a[hn][nx][ny])
f[hn][cf][nx][ny][z] = f[hn][cf][tox][toy][z] + a[hn][nx][ny], Q.push(mp(mp(-f[hn][cf][nx][ny][z],z),mp(nx,ny)));
}
}
}
now = (1 << T) - 1;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
ans = std::min(ans,f[h][(1 << T) - 1][i][j][1]);
printf("%d\n",ans);
}