[WC2008]游览计划
【题目描述】
从未来过绍兴的小 D 有幸参加了Winter Camp 2008,他被这座历史名城的秀丽风景所吸引,强烈要求游览绍兴及其周边的所有景点。
主办者将绍兴划分为 N 行M 列(N×M)个方块,如下图(8×8)
景点含于方块内,且一个方块至多有一个景点。无景点的方块视为路。
为了保证安全与便利,主办方依据路况和治安状况,在非景点的一些方块内安排不同数量的志愿者;在景点内聘请导游(导游不是志愿者)。在选择旅游方案时,保证任意两个景点之间,存在一条路径,在这条路径所经过的每一个方块都有志愿者或者该方块为景点。既能满足选手们游览的需要,又能够让志愿者的总数最少。
例如,在上面的例子中,在每个没有景点的方块中填入一个数字,表示控制
该方块最少需要的志愿者数目:
图中用深色标出的方块区域就是一种可行的志愿者安排方案,一共需要20名志愿者。由图可见,两个相邻的景点是直接(有景点内的路)连通的(如沈园和八字桥)。
现在,希望你能够帮助主办方找到一种最好的安排方案。
【输入格式】
输入文件中 trip.in 中第一行有两个整数,N 和M,描述方块的数目。
接下来 N 行,每行有M 个非负整数,如果该整数为0,则该方块为一个景点;否则表示控制该方块至少需要的志愿者数目。相邻的整数用(若干个)空格隔开,行首行末也可能有多余的空格。
【输出格式】
输出一行一个整数,即最少的志愿者总数。
【样例输入】
4 4 0 1 1 0 2 5 5 1 1 5 5 1 0 1 1 0
【样例输出】
6
【提示】
样例方案:
其中红色方块安排了志愿者。
所有的 10 组数据中N, M ,以及景点数 K 的范围规定如下:
输入文件中的所有整数均不小于 0 且不超过2^16。
题解:
裸斯坦纳树,直接spfa+子集dp,暴力更新就行.
1 #include <algorithm> 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <queue> 8 using namespace std; 9 typedef pair<int,int>par; 10 const int N=12; 11 int n,m,INF,map[N][N],f[N][N][1<<10],S=0,mx[4]={0,0,1,-1},my[4]={1,-1,0,0}; 12 bool vis[N][N]; 13 queue<par>q; 14 void spfa(int k){ 15 for(int i=1;i<=n;i++)for(int j=1;j<=m;j++) 16 if(f[i][j][k]!=INF) 17 q.push(par(i,j)); 18 int x,y,tx,ty; 19 while(!q.empty()){ 20 x=q.front().first;y=q.front().second;q.pop(); 21 for(int i=0;i<4;i++){ 22 tx=x+mx[i];ty=y+my[i]; 23 if(tx<1 || tx>n || ty<1 || ty>m)continue; 24 if(f[x][y][k]+map[tx][ty]<f[tx][ty][k]){ 25 f[tx][ty][k]=f[x][y][k]+map[tx][ty]; 26 if(!vis[tx][ty])q.push(par(tx,ty)),vis[tx][ty]=true; 27 } 28 } 29 vis[x][y]=false; 30 } 31 } 32 void work(){ 33 scanf("%d%d",&n,&m); 34 memset(f,127/3,sizeof(f)); 35 for(int i=1;i<=n;i++) 36 for(int j=1;j<=m;j++){ 37 scanf("%d",&map[i][j]); 38 if(!map[i][j])f[i][j][1<<(S++)]=0; 39 } 40 int states=(1<<S)-1;INF=f[0][0][0]; 41 for(int s=1;s<=states;s++){ 42 for(int i=1;i<=n;i++)for(int j=1;j<=m;j++) 43 for(int k=(k-1)&s;k;k=(k-1)&s){ 44 if(f[i][j][k]+f[i][j][s-k]-map[i][j]<f[i][j][s]) 45 f[i][j][s]=f[i][j][k]+f[i][j][s-k]-map[i][j]; 46 } 47 spfa(s); 48 } 49 int ans=INF; 50 for(int i=1;i<=n;i++) 51 for(int j=1;j<=m;j++) 52 if(f[i][j][states]<ans) 53 ans=f[i][j][states]; 54 printf("%d\n",ans); 55 } 56 int main() 57 { 58 freopen("wc2008_trip.in","r",stdin); 59 freopen("wc2008_trip.out","w",stdout); 60 work(); 61 return 0; 62 }