bzoj2595 [Wc2008]游览计划——斯坦纳树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2595

今天刚学了斯坦纳树,还不太会,写一道题练习一下;

参考了博客:http://www.cnblogs.com/lazycal/archive/2013/08/31/bzoj-2595.html

代码也是模仿着写的,感觉有了更深的理解;

总之,大概就是两种转移方式,合并转移枚举子集即可,最短路转移用 spfa;

还要记录一个 pre 用来找到连通块内的点;

用哈希一样的方法把几个数 pack 起来的写法真神奇啊;

说实话还是有点云里雾里,不过成功写出了第一道斯坦纳树的题,感觉很好!

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
int const maxn=15,inf=0x3f3f3f3f;
int n,m,K,mx,f[maxn][maxn][1<<maxn],a[maxn][maxn],pre[maxn][maxn][1<<maxn];
int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
bool vis[maxn][maxn],inq[maxn*maxn];
queue<int>q;
int pack(int i,int j){return i*10+j;}
int pack2(int i,int j,int s){return i*100000+j*10000+s;}
void unpack(int x,int &i,int &j){i=x/10; j=x%10;}
void unpack2(int x,int &i,int &j,int &s){i=x/100000; j=(x/10000)%10; s=x%10000;}
bool update(int x,int y,int s,int i,int j,int sta,int w)
{
    if(f[x][y][s]>w){f[x][y][s]=w; pre[x][y][s]=pack2(i,j,sta); return 1;}
    return 0;
}
void spfa(int sta)
{
    while(q.size())
    {
        int i,j; 
        unpack(q.front(),i,j); inq[q.front()]=0; q.pop();
        for(int k=0;k<4;k++)
        {
            int x=i+dx[k],y=j+dy[k],tmp;
            if(x==-1||y==-1||x==n||y==m)continue;//!!!
            if(update(x,y,sta,i,j,sta,f[i][j][sta]+a[x][y])&&!inq[tmp=pack(x,y)])//不改变连通性 
                q.push(tmp),inq[tmp]=1;
        }
    }
}
void dfs(int x,int y,int sta)
{
    if(!pre[x][y][sta])return;
    vis[x][y]=1;
    int i,j,s;
    unpack2(pre[x][y][sta],i,j,s);
    dfs(i,j,s);
    if(i==x&&j==y)dfs(i,j,sta-s);//合并转移 
}
void print()
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(!a[i][j])printf("x");
            else if(vis[i][j])printf("o");
            else printf("_");
        }
        printf("\n");
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    memset(f,0x3f,sizeof f);
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
        {
            scanf("%d",&a[i][j]);
            if(!a[i][j])f[i][j][1<<(K++)]=0;
        }
    mx=(1<<K);
    for(int sta=1;sta<mx;sta++)
    {
        for(int i=0;i<n;i++)
            for(int j=0,tmp;j<m;j++)//以(i,j)为媒介 
            {
                for(int s=(sta&(sta-1));s;s=(s-1)&sta)
                    update(i,j,sta,i,j,s,f[i][j][s]+f[i][j][sta-s]-a[i][j]);//点权重复 
                if(f[i][j][sta]!=inf)q.push(tmp=pack(i,j)),inq[tmp]=1;//inq!
            }
        spfa(sta);//更新不同位置的sta
    }
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            if(!a[i][j])
            {
                printf("%d\n",f[i][j][mx-1]);
                dfs(i,j,mx-1);
                print();
                return 0;
            }
}

 

posted @ 2018-07-16 23:32  Zinn  阅读(143)  评论(0编辑  收藏  举报