【模板】斯坦纳树

来源:互联网 发布:淘宝上有创意的店 编辑:程序博客网 时间:2024/04/27 16:55

链接:

#include <stdio.h>int main(){    puts("转载请注明出处[辗转山河弋流歌 by 空灰冰魂]谢谢");    puts("网址:blog.csdn.net/vmurder/article/details/46499973");}

题目:

斯坦纳树
Time Limit: 1 Sec Memory Limit: 128 MB
Description
现在有一个n*m的矩阵,某些元素为0,剩下的元素大于0.
现在你要选择一些元素,使得任意两个为0的元素都能够通过选中的元素四连通.
(注意,若想达到要求,所有的0自身必须被选中.)
那么请问选中元素的和的最小值是多少?

Input
第一行两个整数n,m,表示矩阵的长和宽.
接下来n行,每行m个整数,第i行第j个整数A[i,j]表示矩阵中第i行第j列的元素大小.

Output
输出一行一个整数表示选中元素的最小和.

Sample Input
4 4
0 1 1 0
1 9 9 1
1 9 9 1
0 1 1 0
Sample Output
6

HINT
1<=n,m<=10,2<=num(0)<=10,0<=A[i,j]<=2^16.

题解:

斯坦纳树模板
流程:

枚举状态集S
{
     枚举S的子集s
     {
         更新f[S][1~n]
     }
    f[S][x]<inf 的x入队
     spfa(S)
}

代码:

#include <queue>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define N 105#define P 12#define inf 0x3f3f3f3fusing namespace std;const int dx[]={0,0,1,-1};const int dy[]={1,-1,0,0};struct Eli{    int v,n;}e[N<<2];int head[N],cnt;inline void add(int u,int v){    e[++cnt].v=v;    e[cnt].n=head[u];    head[u]=cnt;}int n,m;int id[P][P],val[N];void build(){    int i,j,k;    int x,y;    scanf("%d%d",&n,&m);    for(i=1;i<=n;i++)for(j=1;j<=m;j++)        scanf("%d",&val[id[i][j]=++cnt]);    cnt=0;    for(i=1;i<=n;i++)for(j=1;j<=m;j++)for(k=0;k<4;k++)        if(id[x=i+dx[k]][y=j+dy[k]])add(id[i][j],id[x][y]);    n*=m,cnt=0;}int f[1<<P][N];queue<int>q;bool in[N];void spfa(int S){    int i,u,v;    while(!q.empty())    {        u=q.front(),q.pop(),in[u]=0;        for(i=head[u];i;i=e[i].n)        {            if(f[S][v=e[i].v]>f[S][u]+val[v])            {                f[S][v]=f[S][u]+val[v];                if(!in[v])q.push(v);            }        }    }}void work(){    int i,j,k;    int S,s;    memset(f,0x3f3f3f3f,sizeof f);    for(i=1;i<=n;i++)if(!val[i])f[1<<cnt++][i]=0;    for(S=1;S<(1<<cnt);S++)    {        for(s=(S-1)&S;s;s=(s-1)&S)for(i=1;i<=n;i++)            f[S][i]=min(f[S][i],f[s][i]+f[S^s][i]-val[i]);        for(i=1;i<=n;i++)if(f[S][i]<inf&&!in[i])q.push(i),in[i]=1;        spfa(S);    }    int ans=inf;    for(i=1;i<=n;i++)ans=min(ans,f[(1<<cnt)-1][i]);    printf("%d\n",ans);}int main(){    freopen("test.in","r",stdin);    build();    work();    return 0;}
0 0
原创粉丝点击