您的位置:首页 > 其它

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

2016-06-23 09:58 330 查看
学一下这个模型,以前一直以为是到插头dp,实质上只是个状压dp

f[i][j][S]表示现在在点(i,j),与(i,j)联通的点的集合为S的最小值

f[i][j][S]=min{f[i][j][s]+f[i][j][S-s]-a[i][j]}

f[i][j][S]=min{f[i'][j'][S]+a[i][j]}

由于同层之间的转移有环,所以用spfa来转移

本题需要记录一下从哪个状态转移来的

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#define inf 1000000000

using namespace std;

int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int f[20][20][1200],g[3][20][20][1200];
int n,m,num;
int a[20][20],bin[20];
int qx[410],qy[410];
bool vis[20][20];
char s[20][20];

void dfs(int x,int y,int S)
{
if (!S) return;
s[x][y]=a[x][y]?'o':'x';
dfs(g[0][x][y][S],g[1][x][y][S],g[2][x][y][S]);
if (g[0][x][y][S]==x && g[1][x][y][S]==y) dfs(x,y,S^g[2][x][y][S]);
}

void solve(int x,int y)
{
printf("%d\n",f[x][y][bin[num]-1]);
for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) s[i][j]='_';
dfs(x,y,bin[num]-1);
for (int i=1;i<=n;i++) printf("%s\n",s[i]+1);
}

int main()
{
scanf("%d%d",&n,&m);
bin[0]=1;for (int i=1;i<=10;i++) bin[i]=bin[i-1]*2;
for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) for (int k=0;k<bin[10];k++) f[i][j][k]=inf;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
if (a[i][j]==0) num++,f[i][j][bin[num-1]]=0;
}
for (int S=1;S<bin[num];S++)
{
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
for (int s=S&(S-1);s;s=(s-1)&S)
{
int tmp=f[i][j][s]+f[i][j][S-s]-a[i][j];
if (tmp<f[i][j][S]) f[i][j][S]=tmp,g[0][i][j][S]=i,g[1][i][j][S]=j,g[2][i][j][S]=s;
}
memset(vis,0,sizeof(vis));
int l=0,r=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (f[i][j][S]!=inf) qx[++r]=i,qy[r]=j,vis[i][j]=1;
while (l!=r)
{
l++;if (l==400) l=0;
int x=qx[l],y=qy[l];
for (int k=0;k<4;k++)
{
int xx=x+dx[k],yy=y+dy[k];
if (xx<1 || yy<1 || xx>n || yy>m) continue;
int tmp=f[x][y][S]+a[xx][yy];
if (tmp<f[xx][yy][S])
{
f[xx][yy][S]=tmp;g[0][xx][yy][S]=x;g[1][xx][yy][S]=y;g[2][xx][yy][S]=S;
if (!vis[xx][yy])
{
r++;if (r==400) r=0;
qx[r]=xx;qy[r]=yy;vis[xx][yy]=1;
}
}
}
vis[x][y]=0;
}
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (a[i][j]==0) {solve(i,j);return 0;}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: