挖宝藏
Solution
这是一个较经典的斯坦纳树模型
就是把一堆点串起来的最小代价。
所以说最小生成树只是斯坦纳树的一个特殊情况.
所以我们可以设f[i][j][k][S]表示在当前(i,j,k)这个位置上,我们在同层已经选了集合为S的点的最小代价
方程可以写成$$f[i][j][k][S]=f[i][j][k][s]+f[i][j][k][S-s]-a[i][j][k];$$
这里要记录一下这个特殊的枚举子集的方法。
普通我们枚举子集都是直接先\(2^n\)再加上\(2^n\)的枚举就是\(4^n\)
但是斯坦纳树的枚举方法是每次把最后面的一个一去掉,然后再&一下原数,补回前面的1
这样就是\(3^n\)
具体代码是这样的
for (s=S&(S-1);s;s=S&(s-1))
Code
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const long long maxn=10+1;
const long long maxs=1<<10;
const long long fx[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
long long h,n,m,tot;
long long f[maxn][maxn][maxn][maxs];
long long a[maxn][maxn][maxn];
bool bz[maxn][maxn];
long long flag[maxn][maxn][maxn];
struct addr{
long long x,y;
}d[maxn*maxn*800],b[maxn][maxn];
long long c[maxn];
void spfa(long long p,long long S){
long long h=0,t=tot;
while(h!=t){
h=h%(maxn*maxn*maxn)+1;
for (long long i=0;i<4;++i){
long long xx=d[h].x+fx[i][0];
long long yy=d[h].y+fx[i][1];
if(xx<=n&&yy<=m&&xx>0&&yy>0)
if(f[p][xx][yy][S]>f[p][d[h].x][d[h].y][S]+a[p][xx][yy]){
f[p][xx][yy][S]=f[p][d[h].x][d[h].y][S]+a[p][xx][yy];
if(!bz[xx][yy]){
t=t%(maxn*maxn*maxn)+1;
d[t]=(addr){xx,yy};
bz[xx][yy]=1;
}
}
}
bz[d[h].x][d[h].y]=0;
}
}
int main(){
freopen("treasure.in","r",stdin);
freopen("treasure.out","w",stdout);
scanf("%lld%lld%lld",&h,&n,&m);
long long i,j,k;
long long s,S;
for (i=1;i<=h*n;++i)
for (j=1;j<=m;++j)
scanf("%lld",&a[(i-1)/n+1][(i-1)%n+1][j]);
for (i=1;i<=h;++i){
scanf("%lld",&c[i]);
for (j=1;j<=c[i];++j){
scanf("%lld%lld",&b[i][j].x,&b[i][j].y);
flag[i][b[i][j].x][b[i][j].y]=j;
}
if(i>1) ++c[i];
}
memset(f,127,sizeof(f));
long long ans=1e17;
for (i=1;i<=h;++i){
for (j=1;j<=n;++j)
for (k=1;k<=m;++k)
if(flag[i][j][k]) f[i][j][k][1<<(flag[i][j][k]-1)]=a[i][j][k];
else f[i][j][k][0]=a[i][j][k];
for (S=0;S<(1<<c[i]);++S){
memset(d,0,sizeof(d));tot=0;
memset(bz,0,sizeof(bz));
for (j=1;j<=n;++j){
for (k=1;k<=m;++k){
for (s=S&(S-1);s;s=S&(s-1))
if(f[i][j][k][s]<1e12&&f[i][j][k][S-s]<1e12)
f[i][j][k][S]=min(f[i][j][k][S],f[i][j][k][s]+f[i][j][k][S-s]-a[i][j][k]);
if(f[i][j][k][S]<1e12){
d[++tot]=(addr){j,k};
bz[j][k]=1;
}
}
}
spfa(i,S);
}
S--;
for (j=1;j<=n;++j)
for (k=1;k<=m;++k){
if(f[i][j][k][S]>1e12) continue;
if(i==h){
ans=min(ans,f[i][j][k][S]);
}
else{
if(flag[i+1][j][k]){
f[i+1][j][k][1<<(c[i+1]-1)|1<<(flag[i+1][j][k]-1)]=f[i][j][k][S]+a[i+1][j][k];
}
else f[i+1][j][k][1<<(c[i+1]-1)]=f[i][j][k][S]+a[i+1][j][k];
}
}
}
printf("%lld\n",ans);
}