【BZOJ2595】 [Wc2008]游览计划
2015-01-10 17:26
393 查看
2595: [Wc2008]游览计划
Time Limit: 10 Sec Memory Limit: 256 MBSec Special JudgeSubmit: 586 Solved: 212
[Submit][Status]
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斯坦纳树。
(用spfa来进行dp转移)
论文:SPFA算法的优化及应用
斯坦纳树问题是组合优化问题,与最小生成树相似,是最短网络的一种。最小生成树是在给定的点集和边中寻求最短网络使所有点连通。而最小斯坦纳树允许在给定点外增加额外的点,使生成的最短网络开销最小。
本题用状压dp。
f[i][j][now]表示根在(i,j)且与他相连的点的状态至少为now(now是一个二进制数,每一位代表一个景点,1表示与(i,j)连通,0表示不连通)的最优解。
dp方程分为两部分。
首先是f[i][j][now]=min(f[i][j][now],f[i][j][s]+f[i][j][now-s]-a[i][j]) ,这一步的转移通过枚举now的子集来转移。
然后是f[i][j][now]=min(f[i][j][now],f[i'][j'][now]+a[i][j]),因为只有某个点的最优值被更新后,才可能去更新他的相邻点,符合spfa的求解过程,用spfa来转移。
本题要求输出方案,记录一下每个点的最优值是从哪一个转移过来的即可。
#include <iostream> #include <queue> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #define inf 0x3f3f3f3f using namespace std; int k,f[15][15][2000],inq[20000],ans[15][15],n,m,a[15][15]; struct point { int i,j; }; struct Record { int i,j,now; }from[15][15][2005]; queue<point> q; const int d[4][2]={{1,0},{-1,0},{0,1},{0,-1}}; int c(point x) { return (x.i-1)*n+x.j; } void spfa(int now) { while (!q.empty()) { point x=q.front(); q.pop(); inq[c(x)]=0; for (int i=0;i<4;i++) { point y; y.i=x.i+d[i][0],y.j=x.j+d[i][1]; if (y.i<1||y.j<1||y.i>n||y.j>m) continue; if (f[y.i][y.j][now]>f[x.i][x.j][now]+a[y.i][y.j]) { f[y.i][y.j][now]=f[x.i][x.j][now]+a[y.i][y.j]; if (!inq[c(y)]) q.push(y),inq[c(y)]=1; from[y.i][y.j][now].i=x.i,from[y.i][y.j][now].j=x.j,from[y.i][y.j][now].now=now; } } } } void dfs(int x,int y,int now) { if (!from[x][y][now].now)return; ans[x][y]=1; dfs(from[x][y][now].i,from[x][y][now].j,from[x][y][now].now); if (from[x][y][now].i==x&&from[x][y][now].j==y) dfs(x,y,now^from[x][y][now].now); } int main() { k=0; scanf("%d%d",&n,&m); memset(f,0x3f,sizeof(f)); 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][1<<(k++)]=0; } if (!k) { cout<<0<<endl; for (int i=1;i<=n;i++) { for (int j=1;j<=m;j++) cout<<"_"; cout<<endl; } return 0; } for (int now=1;now<(1<<k);now++) { for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) { for (int s=now&(now-1);s;s=now&(s-1)) { if (f[i][j][now]>f[i][j][s]+f[i][j][now-s]-a[i][j]) { f[i][j][now]=f[i][j][s]+f[i][j][now-s]-a[i][j]; from[i][j][now].i=i,from[i][j][now].j=j,from[i][j][now].now=s; } } if (f[i][j][now]!=inf) { point x; x.i=i,x.j=j; q.push(x),inq[c(x)]=1; } } spfa(now); } int x,y; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (!a[i][j]) {x=i,y=j;break;} cout<<f[x][y][(1<<k)-1]<<endl; dfs(x,y,(1<<k)-1); for (int i=1;i<=n;i++) { for (int j=1;j<=m;j++) { if (!a[i][j]) cout<<"x"; else if (ans[i][j]) cout<<"o"; else cout<<"_"; } cout<<endl; } return 0; }
感悟:
1.wa是因为n,m写反了
2.spfa转移dp方程!!!
相关文章推荐
- bzoj 2595: [Wc2008]游览计划(斯坦纳树)
- 【bzoj2595】[Wc2008]游览计划 斯坦纳树
- BZOJ_2595_[Wc2008]游览计划_斯坦纳树
- BZOJ 2595 WC 2008 游览计划 斯坦纳树
- BZOJ.2595.[WC2008]游览计划(DP 斯坦纳树)
- [BZOJ]2595 [WC2008] 游览计划 斯坦纳树
- BZOJ 2595: [Wc2008]游览计划 斯坦纳树
- 【BZOJ】【2595】【WC2008】游览计划
- BZOJ 2595 [Wc2008]游览计划
- 【BZOJ2595】【Wc2008】游览计划、斯坦纳树
- BZOJ2595 [Wc2008]游览计划
- 【WC2008】bzoj2595 游览计划
- Bzoj2595: [Wc2008]游览计划
- BZOJ 2595 [Wc2008]游览计划 ——斯坦纳树
- bzoj2595 [Wc2008]游览计划
- BZOJ2595 [Wc2008]游览计划
- BZOJ 2595: [Wc2008]游览计划(斯坦纳树)
- 【BZOJ 2595】【WC 2008】游览计划
- BZOJ 2595: [Wc2008]游览计划
- bzoj2595 [Wc2008]游览计划