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
具体代码是这样的
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); }
在这里插入图片描述
在这里插入图片描述
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];
f[i][j][k][S]=f[i][j][k][s]+f[i][j][k][S−s]−a[i][j][k];
这里要记录一下这个特殊的枚举子集的方法。
普通我们枚举子集都是直接先2^n2
n
再加上2^n2
n
的枚举就是4^n4
n
但是斯坦纳树的枚举方法是每次把最后面的一个一去掉,然后再&一下原数,补回前面的1
具体代码是这样的
for (s=S&(S-1);s;s=S&(s-1))
Code
include
include
include
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[maxnmaxn800],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%(maxnmaxnmaxn)+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%(maxnmaxnmaxn)+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);
}