bzoj 2595: [Wc2008]游览计划(斯坦纳树)
2016-05-28 07:57
239 查看
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
4 40 1 1 0
2 5 5 1
1 5 5 1
0 1 1 0
Sample Output
6xoox
___o
___o
xoox
HINT
对于100%的数据,N,M,K≤10,其中K为景点的数目。输入的所有整数均在[0,2^16]的范围内Source
Ljcc930提供SPJ[Submit][Status][Discuss]
题解:斯坦纳树就是包含给定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 ,vis ,mi ; int f [(1<<N)],k1,can ; struct data{ int x,y,thd; }pre [(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"); } }
相关文章推荐
- <读书笔记>软件调试之道 :问题的核心-如何修复缺陷
- 第二次冲刺站立会议04
- logback logback.xml常用配置详解(二)<appender>
- 第二次冲刺阶段第五天
- Linux守护进程的启动方法
- SQL 创建存储过程PROCEDURE
- AngularJS 实现简单购物车
- 并发问题分析
- 第二次冲刺个人博客04
- 第17课:spark streming资源动态申请和动态控制消费速率原理剖析
- logback常用配置详解(一)<configuration> and <logger>
- 模仿通讯录侧滑栏
- 关于tomcat启动报“this web application instance has been stopped already”的处理
- 经典.net面试题目
- RF射频技术的原理
- adb.exe 命令详解
- 怎么解决的dedecms无法保存远程图片到本地服务器
- Java 导出 execl
- opencv矩的概念
- c语言中char的-128是怎么计算的