一叶浮萍归大海

人生何处不相逢

挖宝藏

在这里插入图片描述

在这里插入图片描述

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);
}
posted @ 2019-08-14 08:27  Chandery  阅读(213)  评论(0编辑  收藏  举报