【BZOJ 2595】2595: [Wc2008]游览计划 (状压DP+spfa,斯坦纳树?)

2595: [Wc2008]游览计划

Time Limit: 10 Sec  Memory Limit: 256 MBSec  Special Judge
Submit: 1572  Solved: 739

Description

Input

第一行有两个整数,N和 M,描述方块的数目。 
接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点;
否则表示控制该方块至少需要的志愿者数目。 相邻的整数用 (若干个) 空格隔开,
行首行末也可能有多余的空格。

Output

由 N + 1行组成。第一行为一个整数,表示你所给出的方案
中安排的志愿者总数目。 
接下来 N行,每行M 个字符,描述方案中相应方块的情况: 
z  ‘_’(下划线)表示该方块没有安排志愿者; 
z  ‘o’(小写英文字母o)表示该方块安排了志愿者; 
z  ‘x’(小写英文字母x)表示该方块是一个景点; 
注:请注意输出格式要求,如果缺少某一行或者某一行的字符数目和要求不
一致(任何一行中,多余的空格都不允许出现) ,都可能导致该测试点不得分。

Sample Input

4 4

0 1 1 0

2 5 5 1

1 5 5 1

0 1 1 0

Sample Output

6

xoox

___o

___o

xoox

HINT

对于100%的数据,N,M,K≤10,其中K为景点的数目。输入的所有整数均在[0,2^16]的范围内

Source

Ljcc930提供SPJ

【分析】

  又是不会的一题啦~

  斯坦纳树?smg?

  刚开始看题可能觉得是最小生成树吧?但是并不是的。

  

  一个条路径可能在最小生成树上面算几次,但是在这题上只算一次。

  然后就是斯坦纳树??【长姿势??

  反正就是,要覆盖的点很少<=10,可以状压这个,f[i][j][t]表示和(i,j)这个格子联通的需覆盖点集合为t的最小代价。

  两个方程:

  $f[i][j][t]=min(f[i][j][s]+f[i][j][t-ss])$ s是t的子集。

  $f[i][j][t]=min(f[x][y][t]+a[i][j])$ (i,j)与(x,y)相邻

  第二个式子啊不是普通的dp啊,转移状态的有环的!!但是,不怕,肯定是小的转到大的,然后一脸最短路的样子,就可以用spfa解决的。

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 #include<queue>
  7 using namespace std;
  8 #define INF 0xfffffff
  9
 10 int a[15][15],num[15][15],f[15][15][1500];
 11
 12 struct node {int x,y,d;};
 13 node g[15][15][1500];
 14 queue<node > q;
 15 bool inq[15][15];
 16
 17 int bx[6]={0,1,0,-1,0},
 18     by[6]={0,0,1,0,-1};
 19
 20 void dfs(int x,int y,int k)
 21 {
 22     if(!k) return;inq[x][y]=1;
 23     dfs(g[x][y][k].x,g[x][y][k].y,g[x][y][k].d);
 24     if(g[x][y][k].x==x&&g[x][y][k].y==y) dfs(x,y,k-g[x][y][k].d);
 25 }
 26
 27 int main()
 28 {
 29     int n,m,cnt=0;
 30     scanf("%d%d",&n,&m);
 31     memset(f,63,sizeof(f));
 32     for(int i=1;i<=n;i++)
 33      for(int j=1;j<=m;j++)
 34      {
 35          scanf("%d",&a[i][j]);
 36          if(a[i][j]==0)
 37          {
 38              num[i][j]=++cnt;
 39              f[i][j][1<<cnt-1]=0;
 40              g[i][j][1<<cnt-1].x=g[i][j][1<<cnt-1].y=g[i][j][1<<cnt-1].d=0;
 41          }
 42      }
 43     for(int i=1;i<=n;i++)
 44      for(int j=1;j<=m;j++) f[i][j][0]=0;
 45     for(int k=1;k<=(1<<cnt)-1;k++)
 46     {
 47         memset(inq,0,sizeof(inq));
 48         for(int ss=k;ss;ss=(ss-1)&k)
 49         {
 50             for(int i=1;i<=n;i++)
 51              for(int j=1;j<=m;j++) //if(a[i][j]==0)
 52              {
 53                  if(f[i][j][k]>f[i][j][ss]+f[i][j][k-ss]-a[i][j])
 54                 {
 55                     f[i][j][k]=f[i][j][ss]+f[i][j][k-ss]-a[i][j];
 56                     node nw;
 57                     nw.x=i;nw.y=j;nw.d=ss;
 58                     g[i][j][k]=nw;
 59                 }
 60                 if(f[i][j][k]<INF) {node nw;nw.x=i;nw.y=j;nw.d=f[i][j][k];inq[i][j]=1;q.push(nw);}
 61              }
 62
 63         }
 64         while(!q.empty())
 65         {
 66             node x=q.front();
 67             for(int i=1;i<=4;i++)
 68             {
 69                 int nx=x.x+bx[i],ny=x.y+by[i];
 70                 if(nx<1||nx>n||ny<1||ny>m) continue;
 71                 if(f[nx][ny][k]>f[x.x][x.y][k]+a[nx][ny])
 72                 {
 73                     f[nx][ny][k]=f[x.x][x.y][k]+a[nx][ny];
 74                     node nw;
 75                     nw.x=nx;nw.y=ny;//nw.d=f[nx][ny][k];
 76                     // g[nx][ny][k]=g[x.x][x.y][k];
 77                     g[nx][ny][k].x=x.x;g[nx][ny][k].y=x.y;g[nx][ny][k].d=k;
 78                     if(!inq[nx][ny])
 79                     {
 80                         inq[nx][ny]=1;
 81                         q.push(nw);
 82                     }
 83                 }
 84             }
 85             q.pop();inq[x.x][x.y]=0;
 86         }
 87     }
 88     memset(inq,0,sizeof(inq));
 89     bool ok=0;
 90     for(int i=1;i<=n;i++)
 91     {
 92         for(int j=1;j<=m;j++) if(a[i][j]==0)
 93         {
 94             printf("%d\n",f[i][j][(1<<cnt)-1]);
 95             dfs(i,j,(1<<cnt)-1);
 96             ok=1;break;
 97         }
 98         if(ok) break;
 99     }
100
101     for(int i=1;i<=n;i++)
102     {
103         for(int j=1;j<=m;j++)
104         {
105             if(a[i][j]==0) printf("x");
106             else if(inq[i][j]) printf("o");
107             else printf("_");
108         }
109         printf("\n");
110     }
111     return 0;
112 }

2017-04-05 19:28:31

时间: 2024-10-14 02:31:37

【BZOJ 2595】2595: [Wc2008]游览计划 (状压DP+spfa,斯坦纳树?)的相关文章

HDU 4284 状压dp+spfa堆优化

题意: 给定n个点 m条无向边 d元. 下面m行表示每条边 u<=>v 以及花费 w 下面top 下面top行 num c d 表示点标为num的城市 工资为c 健康证价格为d 目标是经过给定的top个城市,当到达该城市时,必须马上购买该城市的健康证并打工赚钱(每个城市只打工1次) 问从1城市出发,最后回到1城市,能否收集到所有的健康证 思路: 由于top很小,所以状压dp dp[i][tmp]表示当前处于i点 经过城市的状态为tmp时 身上最多的钱. 首先对dis数组floyd 跑出最短路,

[BZOJ 1072] [SCOI2007] 排列perm 【状压DP】

题目链接:BZOJ 1072 这道题使用 C++ STL 的 next_permutation() 函数直接暴力就可以AC .(使用 Set 判断是否重复) 代码如下: #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cmath> #include <set>

BZOJ 1087: [SCOI2005]互不侵犯King( 状压dp )

简单的状压dp... dp( x , h , s ) 表示当前第 x 行 , 用了 h 个 king , 当前行的状态为 s . 考虑转移 : dp( x , h , s ) = ∑ dp( x - 1 , h - cnt_1( s ) , s' ) ( s and s' 两行不冲突 , cnt_1( s ) 表示 s 状态用了多少个 king ) 我有各种预处理所以 code 的方程和这有点不一样 ------------------------------------------------

BZOJ 3446: [Usaco2014 Feb]Cow Decathlon( 状压dp )

水状压dp. dp(x, s) = max{ dp( x - 1, s - {h} ) } + 奖励(假如拿到的) (h∈s). 时间复杂度O(n * 2^n) ---------------------------------------------------------------------------------- #include<bits/stdc++.h> #define rep(i, n) for(int i = 0; i < n; ++i) #define clr(x

BZOJ 1879 [Sdoi2009]Bill的挑战 ——状压DP

本来打算好好写写SDOI的DP题目,但是忒难了, 太难了,就写的这三道题仿佛是可做的. 生在弱省真是兴奋. 这题目直接状压,f[i][j]表示匹配到i,状态集合为j的方案数,然后递推即可. #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define F(i,j,k) for (int i=j;i<=k

BZOJ 1076 SCOI2008 奖励关 期望状压DP

题目大意:给定k次弹出宝物的机会,每次随机弹出n种宝物的机会,如果吃过这种宝物的所有前提宝物就可以吃这种宝物,求最优策略的期望得分 看到数据范围果断状压DP- - 不看数据范围害死人- - 至于吃还是不吃 这是个问题 对于这种最优策略的期望DP 我们一般都是从后往前推 枚举每次出现宝物 枚举此时的状态 枚举宝物是哪种 如果当前的宝物可以吃 就在吃与不吃的后继状态中选择最大值加到当前状态上 如果当前的宝物不能吃 只能选择不吃的后继状态加到当前状态上 最后输出f[1][0]就是答案 #include

BZOJ 4197 NOI 2015 寿司晚宴 状压DP

4197: [Noi2015]寿司晚宴 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 694  Solved: 440[Submit][Status][Discuss] Description 为了庆祝 NOI 的成功开幕,主办方为大家准备了一场寿司晚宴.小 G 和小 W 作为参加 NOI 的选手,也被邀请参加了寿司晚宴. 在晚宴上,主办方为大家提供了 n?1 种不同的寿司,编号 1,2,3,-,n?1,其中第 i 种寿司的美味度为 i+1 (即

bzoj 1556: 墓地秘密【状压dp+spfa】

显然是状压,显然不可能把所有格子压起来 仔细观察发现只有机关周围的四个格子有用以及起点,所以我们用spfa处理出这些格子两两之间的距离(注意细节--这里写挂了好几次),然后设f[s][i]为碰完的机关石状态为s,现在在有用格子的第i个的最小停下次数,转移按照套路即可 #include<iostream> #include<cstdio> #include<queue> #include<cstring> using namespace std; const

bzoj 4197: [Noi2015]寿司晚宴【状压dp】

一个数内可能多个的质因数只有小于根号n的,500内这样的数只有8个,所以考虑状压 把2~n的数处理出小于根号500的质因数集压成s,以及大质数p(没有就是1),然后按p排序 根据题目要求,拥有一个质因数的只能给一个人,所以排序后能给一个人的大质数就是一个区间 然后设f[s1][s2]为一人选s1,另一人选s2的方案数,注意这里的s只压了小于根号500的八个质数 设g[0/1][s1][s2]为一人选s1,另一人选s2的,当前枚举的大质数给小G/小W的方案数 正常转移即可 然后注意把g转到f上时应