• 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("
    ");
        }
    }
    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
    ",f[i][j][mx-1]);
                    dfs(i,j,mx-1);
                    print();
                    return 0;
                }
    }
  • 相关阅读:
    Arrays类总结
    多维数组
    数组
    写一个计算器,要求实现加减乘除功能,能够循环接收收据,通过用户交互实现
    递归
    方法
    函数
    流程控制
    mysql笔记(连接与子查询部分)
    ubuntu下mysql的常用命令
  • 原文地址:https://www.cnblogs.com/Zinn/p/9321088.html
Copyright © 2020-2023  润新知