2595: [Wc2008]游览计划
Time Limit: 10 Sec Memory Limit: 256 MBSec Special JudgeSubmit: 1253 Solved: 574
[ Submit][ Status][ Discuss]
Description
Input
第一行有两个整数,N和 M,描述方块的数目。
接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点;
否则表示控制该方块至少需要的志愿者数目。 相邻的整数用 (若干个) 空格隔开,
行首行末也可能有多余的空格。
Output
由 N + 1行组成。第一行为一个整数,表示你所给出的方案
中安排的志愿者总数目。
接下来 N行,每行M 个字符,描述方案中相应方块的情况:
z ‘_’(下划线)表示该方块没有安排志愿者;
z ‘o’(小写英文字母o)表示该方块安排了志愿者;
z ‘x’(小写英文字母x)表示该方块是一个景点;
注:请注意输出格式要求,如果缺少某一行或者某一行的字符数目和要求不
一致(任何一行中,多余的空格都不允许出现) ,都可能导致该测试点不得分。
Sample Input
0 1 1 0
2 5 5 1
1 5 5 1
0 1 1 0
Sample Output
xoox
___o
___o
xoox
HINT
对于100%的数据,N,M,K≤10,其中K为景点的数目。输入的所有整数均在[0,2^16]的范围内
Source
题解:斯坦纳树就是包含给定K个节点的最小生成树(貌似也可以生成森林)。
dp[i][j] 表示到达i这个点,当前状态为j ,注意第二维是状压,用来表示给定点是否被选。
枚举子树的形态:dp[ i ][ j ]=min{ dp[ i ][ j ],dp[ i ][ k ]+dp[ i ][ l ] },其中k和l是对j的一个划分。
按照边进行松弛:dp[ i ][ j ]=min{ dp[ i ][ j ],dp[ i' ][ j ]+w[ i ][ i' ] },其中i和i'之间有边相连,利用spfa 进行松弛。
总复杂度为O(n*3^k)
进行spfa的时候只需要对当前层的节点进行spfa就行了,不需要整个图完全松弛一遍,因为更高的层都可以通过枚举子集而变成若干个更低的层,这样一次spfa的复杂度一下就降了下来,变成了O(n)级别
http://endlesscount.blog.163.com/blog/static/821197872012525113427573/ 这里有更详细的介绍。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define N 13
#define LL long long
#define pa pair<int,int>
#define inf 1000000000
using namespace std;
int n,m,a[N][N],vis[N][N],mi[N];
int f[N][N][(1<<N)],k1,can[N][N];
struct data{
int x,y,thd;
}pre[N][N][(1<<N)];
int x[10]={0,0,-1,1},y[10]={-1,1,0,0};
queue<pa> q;
void spfa(int sta)
{
while (!q.empty())
{
int nowx=q.front().first; int nowy=q.front().second;
q.pop();
for (int i=0;i<4;i++)
{
int xx=nowx+x[i]; int yy=nowy+y[i];
if (xx<=0||yy<=0||xx>n||yy>m) continue;
if (f[xx][yy][sta]>f[nowx][nowy][sta]+a[xx][yy])
{
f[xx][yy][sta]=f[nowx][nowy][sta]+a[xx][yy];
pre[xx][yy][sta]=(data){nowx,nowy,sta};
if (!can[xx][yy]){
can[xx][yy]=1;
q.push(make_pair(xx,yy));
}
}
}
can[nowx][nowy]=0;
}
}
void dfs(int x,int y,int sta)
{
if (x==inf||pre[x][y][sta].thd==0) return;
vis[x][y]=1;
data t=pre[x][y][sta];
dfs(t.x,t.y,t.thd);
if (t.x==x&&t.y==y) dfs(x,y,sta-t.thd);
}
int main()
{
scanf("%d%d",&n,&m);
mi[0]=1;
for (int i=1;i<=11;i++) mi[i]=mi[i-1]*2;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
for (int k=0;k<mi[11];k++)
f[i][j][k]=pre[i][j][k].x=inf;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
if (!a[i][j]) f[i][j][mi[k1]]=0,k1++;
}
for (int sta=1;sta<mi[k1];sta++)
{
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
for (int s=sta&(sta-1);s;s=sta&(s-1))
{
int t=f[i][j][s]+f[i][j][sta-s]-a[i][j];
if (t<f[i][j][sta])
{
f[i][j][sta]=t;
pre[i][j][sta]=(data){i,j,s};
}
}
if (f[i][j][sta]<inf)
q.push(make_pair(i,j)),can[i][j]=1;
}
spfa(sta);
}
int x,y;
for (int i=1;i<=n;i++)
{
bool pd=false;
for (int j=1;j<=m;j++)
if (!a[i][j]){
x=i; y=j; pd=true; break;
}
if (pd) break;
}
printf("%d\n",f[x][y][mi[k1]-1]);
dfs(x,y,mi[k1]-1);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
if (!a[i][j]) printf("x");
else if (vis[i][j]) printf("o");
else printf("_");
if (j==m) printf("\n");
}
}