正题
题目大意
hhh层,每层n∗mn*mn∗m个石头,挖开不同位置的石头有不同的消耗,只能从高层下到低层。有一些宝藏,求拿到所有宝藏的最小代价。
解题思路
先考虑只有111层的情况,因为挖开的不用再挖 ,我们可以发现本题就是一个斯坦纳树,我们用SSS状压宝藏的拿取情况。
然后有动态转移方程
fS,x,y={fS′,x,y+fS−S′,x,y−ax,y(S′∈S)fS,x−1,y+ax,yfS,x+1,y+ax,yfS,x,y−1+ax,yfS,x,y+1+ax,yf_{S,x,y}=\left\{\begin{matrix} f_{S',x,y}+f_{S-S',x,y}-a_{x,y}(S'\in S) \\ f_{S,x-1,y}+a_{x,y} \\ f_{S,x+1,y}+a_{x,y} \\ f_{S,x,y-1}+a_{x,y} \\ f_{S,x,y+1}+a_{x,y} \end{matrix}\right.fS,x,y=⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧fS′,x,y+fS−S′,x,y−ax,y(S′∈S)fS,x−1,y+ax,yfS,x+1,y+ax,yfS,x,y−1+ax,yfS,x,y+1+ax,y
本方程有后效性且与最短路形式较为相似所以我们枚举SSS先计算第一个后跑一遍SPFASPFASPFA即可
然后我们考虑多层时从下往上递推,先将fff多开一维表示层。然后我们可以将上层多开一个宝藏点表示是否选择了下层的所有宝藏
然后有递推式fz,1,x,y=fz+1,2kz+1+1−1,x,yf_{z,1,x,y}=f_{z+1,2^{k_{z+1}+1}-1,x,y}fz,1,x,y=fz+1,2kz+1+1−1,x,y
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
const ll dx[4]={1,-1,0,0},dy[4]={0,0,1,-1},N=15,M=1<<10;
ll h,n,m,g[N][N][N],f[N][M][N][N],ans,a[N][N][N],k[N];
bool v[N][N];
queue<int> qx,qy;
void Spfa(ll z,ll s)
{for(ll x=1;x<=n;x++)for(ll y=1;y<=m;y++)qx.push(x),qy.push(y),v[x][y]=1;while(!qx.empty()){ll x=qx.front(),y=qy.front();qx.pop();qy.pop();for(ll i=0;i<4;i++){ll zx=x+dx[i],zy=y+dy[i];if(zx<1||zy<1||zx>n||zy>m) continue;if(f[z][s][x][y]+a[z][zx][zy]>=f[z][s][zx][zy]) continue;f[z][s][zx][zy]=f[z][s][x][y]+a[z][zx][zy];if(!v[zx][zy]){v[zx][zy]=1;qx.push(zx);qy.push(zy);} }v[x][y]=0;}
}
int main()
{freopen("treasure.in","r",stdin);freopen("treasure.out","w",stdout);scanf("%lld%lld%lld",&h,&n,&m);for(ll i=1;i<=h;i++)for(ll j=1;j<=n;j++)for(ll k=1;k<=m;k++)scanf("%lld",&a[i][j][k]);memset(f,127,sizeof(f));memset(f[h+1],0,sizeof(f[h+1]));for(ll z=1;z<=h;z++){scanf("%lld",&k[z]); for(ll i=0;i<k[z];i++){ll x,y;scanf("%lld%lld",&x,&y);g[z][x][y]|=1<<i;}}for(ll z=h;z>=1;z--){ll LMS=(1<<k[z+1])-1,MS=(1<<k[z]);for(ll i=1;i<=n;i++)for(ll j=1;j<=m;j++)f[z][MS+g[z][i][j]][i][j]=f[z+1][LMS][i][j]+a[z][i][j],f[z][g[z][i][j]][i][j]=a[z][i][j];MS=(1<<(++k[z]))-1;for(ll s=0;s<=MS;s++){for(ll i=1;i<=n;i++)for(ll j=1;j<=m;j++){int l=s;while(l){l=((l-1)&s);if(!l) break;if(max(f[z][l][i][j],f[z][s-l][i][j])<1e17)f[z][s][i][j]=min(f[z][s][i][j],f[z][l][i][j]+f[z][s-l][i][j]-a[z][i][j]);}}Spfa(z,s);}}ans=1e17;ll MS=(1<<k[1])-1;for(ll i=1;i<=n;i++)for(ll j=1;j<=m;j++)ans=min(ans,f[1][MS][i][j]);printf("%lld",ans);
}