挖宝藏

在这里插入图片描述

在这里插入图片描述

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);

}

猜你喜欢

转载自www.cnblogs.com/Chandery/p/11349672.html