• 挖宝藏


    在这里插入图片描述

    在这里插入图片描述

    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
    ",ans);
    }
    
  • 相关阅读:
    Android开发 将数据保存到SD卡
    Android手机拨打电话的开发实例
    Android动画的实现 上
    Windows 7旗舰版搭建andriod 4.0开发环境记录
    [转载]Android开发常用调试技术记录
    暂停和恢复Activity Android
    Android传感器编程带实例
    用VS2010开发Android应用的配置方法
    安卓Activity界面切换添加动画特效
    在安卓开发中使用SQLite数据库操作实例
  • 原文地址:https://www.cnblogs.com/Chandery/p/11349672.html
Copyright © 2020-2023  润新知