试题 算法提高 Building Bridges
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
新奥德卫莱城市委员会计划建造一个连接系统来连接所有市中心的建筑,这样人们从一个建筑去另一个建筑时就不用走到外面了。你要写一个程序来帮忙确定建造方案。
新奥德卫莱市是正方形网状布局,每个建筑物占着一些连通的格子,两个有建筑物的格子如果有角接触就算相连,不需要连接道路。道路只能在正方形的边上建造,并且只能是直线且连接两个建筑。
给你一个建筑示意图,你要算出连接所有建筑的最小道路数。如果这是不可能的,找出最少的无法连接的建筑群数。在可连接所有建筑物的情况下,如果多种方案道路数都是最小的,选择在网格中道路总长度最小的方案。道路可以互相穿过,但在这种情况下道路被判定互不相干,之间无法连接。
下图说明了4种可能的城市配置。城市1包含5个建筑,被4条道路连接起来,总长度为4。城市2中,因没有建筑共享网格线所以无法建立道路。在城市3中,因为只有1个建筑所以我们不需要道路。在城市4中,最好方案是用1条长度为1的道路连接两个城市,剩下两个不连通的建筑群(一个是两个建筑,一个是一个建筑)。
左上是城市1 ,中上是建路后的城市1 ,右上是不能建路的城市2,左下是不需要建路的城市3,中下是城市4,右下是建路后的城市4。
图略。
输入格式
输入包含多座城市,每个城市的描述第一行有2个整数r和c(1<=r,c<=50),表示这个城市的南北距离和东西距离,之后接着r行,每行有c个“#”或“.”,每个字符表示网格中的一个格子。“#”表示那个格子中有建筑,“.” 表示那个格子中没有建筑。数据的最后一个城市是由两个0组成。
输出格式
对于每个城市,像样例那样输出两到三行。第一行是城市编号。如果城市只有少于两个建筑,就在第二行输出“No bridges are needed.”。如果城市有两个及以上个建筑且任意两个建筑都不能被连接,就在第二行输出“No bridges are possible.”。否则,就在第二行输出“N bridges of total length L”,其中N是道路数,L是最优方案的道路长度。(如果N是1,就用bridge代替bridges)。如果最终方案剩下了多个建筑群,在第三行输出建筑群数。
每组数据间用空行隔开。见下面样例。
样例输入
3 5
#...#
..#..
#...#
3 5
##...
.....
....#
3 5
#.###
#.#.#
###.#
3 5
#.#..
.....
....#
0 0
样例输出
City 1
4 bridges of total length 4
City 2
No bridges are possible.
2 disconnected groups
City 3
No bridges are needed.
City 4
1 bridge of total length 1
2 disconnected groups
解题思路:建立图,然后用Dijkstra算法。然而,数据的初始化异常繁琐,代码量巨大,如何在建筑间建桥,如何判断城市群数。。。都需要画图模拟找规律,找方法。我的方法是,将当前每个点上的建筑看做城市的中心区,把ta周围的建筑看成环形,能和ta建桥的只有ta外围的二环,权值为1;这里为了利于Dijkstra算法操作,我在当前点与ta的外围的一环的建筑之间也建桥,权值为0。此外,我把每个点(建筑)都存放到向量BDS中,把每座桥都存放到向量BGS中,便于Dijkstra 算法时的访问。这道题,第一天,看了没什么头绪;第二天傍晚,再拿出来,才想到这个方法,然而代码一写就写了一晚上,写到22:00,最后跑了一下,样例输入,跑的输出是对的,但提交后跑的结果是运行错误。写了一晚上,怎么也得给自己个交代,所以写下这篇经验贴,待日后改进。。。如有大佬看到,请多指教,有啥好的方法啊。。。我这代码量太大了。。。手动狗头。。。
代码如下:
#include <iostream>
#include <vector>
#include <string.h>
#include <cmath>
#include <queue>
using namespace std;
struct Budn{
int No;//第No个建筑
int x,y;//第No个建筑 的坐标
Budn(int xx,int yy,int nn):x(xx),y(yy),No(nn){ }
Budn(){ }
};
struct Bridge{
int from,to;
int val;
Bridge(int ff,int tt,int vv):from(ff),to(tt),val(vv){ }
Bridge(){ }
};
struct Resu{
int cnt,res;
Resu(int cc,int rr):cnt(cc),res(rr){ }
Resu(){ }
};
vector<Budn> BDS;
vector<Bridge> BGS;
vector<Resu> vkt;
int G[55][55];
int r,c;
int vis[2510];
int built[52][52];//built[i][j] =1,表示建筑i 和 建筑j 之间已建桥 ;反之 为 0
int d[2510];
int pred[2510];
void init(){
memset(vis,0,sizeof(vis));
memset(built,0,sizeof(built));
BDS.clear();
BGS.clear();
}
void Read(){
for(int i=1;i<=r;i++){
for(int j=1;j<=c;j++){
char cx;
cin>>cx;
if(cx=='.')
G[i][j]=0; //0 表示那个格子中 没有建筑
if(cx=='#'){
G[i][j]=1; //1 表示那个格子中 有建筑
BDS.push_back(Budn(i,j,BDS.size()));
}
}
}
//初始化d
for(int i=0;i<BDS.size();i++){
d[i]=65535;
pred[i]=i;
}
}
void buildBrige(Budn B1,Budn B2){
if( ( abs(B1.x-B2.x)==2 && abs(B1.y-B2.y)<=1 ) || ( abs(B1.y-B2.y)==2 && abs(B1.x-B2.x)<=1 ) ){
//若B2在B1的二环,则可建桥
BGS.push_back(Bridge(B1.No,B2.No,1));
built[B1.No][B2.No]=1;
}
else if( ( abs(B1.x-B2.x)==1 && abs(B1.y-B2.y)<=1 ) || ( abs(B1.y-B2.y)==1 && abs(B1.x-B2.x)<=1 ) ){
//若B2在B1的一环环,则无需建桥,单位了记录,我们建虚拟桥,val值设为0
BGS.push_back(Bridge(B1.No,B2.No,0));
built[B1.No][B2.No]=1;
}
}
void buildAll(){//把所有能建的桥 先建起来,也就是 实例化 所有的路,以便于 Dijkstra算法 长出 最小生成树
for(int i=0;i<BDS.size();i++){
for(int j=0;j<BDS.size();j++){
if(i!=j && built[i][j]==0 && built[j][i]==0 ){
buildBrige(BDS[i],BDS[j]);
}
}
}
}
int Dijkstra(int S){
priority_queue<pair<int,int> > Q;
d[S]=0;
Q.push(make_pair(-d[S],S));
vector<int> occured;
int res=0;
while(!Q.empty()){
int now=Q.top().second;
Q.pop();
occured.push_back(now);
// cout<<now<<","<<d[now]<<" -> ";
for(int i=0;i<BGS.size();i++){
int V=-1;
if(BGS[i].from==now){
V=BGS[i].to;
}
if(BGS[i].to==now){
V=BGS[i].from;
}
if(V!=-1){
if( d[now]+BGS[i].val<d[V]){
pred[V]=now;
vis[V]=1;
d[V]=d[now]+BGS[i].val;
// res+=BGS[i].val;
Q.push(make_pair(-d[V],V));
}
}
}
}
for(int i=0;i<occured.size();i++){
int t=occured[i];
if(pred[t]!=t)
res+=d[t]-d[pred[t]];
}
return res;
}
void mainOpt(){
int res=0;//所有桥的长度
int cnt=0;//不联通的建筑群个数
for(int i=0;i<BDS.size();i++){
if(vis[i]==0){
res+=Dijkstra(i);
cnt++;
}
}
vkt.push_back(Resu(cnt,res));
}
void checkBasicOpt(){
for(int i=0;i<BDS.size();i++){
cout<<"Building NO."<<BDS[i].No<<" ("<<BDS[i].x<<","<<BDS[i].y<<")"<<endl;
}
cout<<"输出所有可建的桥,共有"<<BGS.size()<<endl;
for(int i=0;i<BGS.size();i++){
cout<<"("<<BDS[BGS[i].from].x<<","<<BDS[BGS[i].from].y<<")"<<" <- "<<BGS[i].val<<" -> "<<"("<<BDS[BGS[i].to].x<<","<<BDS[BGS[i].to].y<<")"<<endl;
}
cout<<"---------dividing line-----------"<<endl;
}
int main(int argc, char** argv) {
while(1){
cin>>r>>c;
if(r==0 && c==0)
break;
init();
Read();
buildAll();
// checkBasicOpt();
mainOpt();
}
for(int i=0;i<vkt.size();i++){
int cnt=vkt[i].cnt;
int res=vkt[i].res;
cout<<"City "<<i+1<<endl;
if(cnt==1 && res==0){// 1个建筑
cout<<"No bridges are needed."<<endl;
}
if(cnt>1 && res==0){//2个及2个以上的建筑群
cout<<"No bridges are possible."<<endl;
cout<<cnt<<" disconnected groups"<<endl;
}
if(cnt>1 && res>0){
if(res==1)
cout<<"1 bridge of total length 1"<<endl;
if(res>1)
cout<<res<<" bridges of total length "<<res<<endl;
cout<<cnt<<" disconnected groups"<<endl;
}
if(cnt==1 && res>0){//多个建筑,图通路
if(res==1)
cout<<"1 bridge of total length 1"<<endl;
if(res>1)
cout<<res<<" bridges of total length "<<res<<endl;
}
cout<<endl;
}
return 0;
}