昨天在topcoder的网站上看到关于TCHS32 的解题报告,前2题很简单,关键是第三题SuperPasture,此题分数最高1000,时间有限。于是我也试着做了一下,调试花了N长时间
以下是我翻译的题目:
Farmer Brown 有很多方形的牧地(pasture)。当2个牧地共享一条边时我们说这2个牧地是连接起来的(2个牧地只共享个顶点时,这2个牧地不是连接起来的)。一个super pasture是有连接起来的牧地组成的最大的集合(也是一个方形)。也就是说,对于一个super pasture中的任意2牧地,存在一个连接的通路来连接这2个牧地,并且这条通路不会离开这个super pasture。如果一个牧地没有与任何其它的牧地相连接,那么我们说这个牧地本身就是一个super pasture。
上图含有9个牧地,组成了3个super pasture,牧地1,2,3,5,7组成了一个super pature.注意牧地0和牧地6没有连接起来因为他们仅仅只共享了一个角(corner)。
一个super pasture的badness是这个super pasture的面积减去这个区块中所有牧地的面积和的差。比如,如果一个super pasture仅仅有一个牧地组成,那么它的badness就为0. 上图中3个super pasture的badness 分别为0,10,5
Farmer Brown现在决定卖掉一个牧地,他按照下面的规则来选择卖出的牧地:
1.这个牧地必须属于badness最大的super pasture,如果有多个这样的super pasture,那么要对每个这样的super pasture进行一下的规则.
2.当把这个牧地卖掉之后,不能使包含这个牧地的super pasture分裂成开
3.在所有满足1,2条件的牧地中,他将选择面积最小的牧地,如果有多个这样的牧地,那么他将选择序号最小的牧地
给你一个字符串数组pastures(String[] pastures),其中包含Farmer Brown拥有的牧地的信息。数组中的每个元素都是这样的格式"a b c d",其中a b c d分别代表牧地的最左边,最右边(比真实的大1),最上边,最下边(比真实的大1)的坐标。也就是说,这个方块牧地可以用2个坐标来表示(a,c)和(b-1,d-1)。你的程序返回的牧地编号必须是从下表为0开始的结果。
定义:
类: SuperPasture
方法: whichOne
参数: String[]
返回: int
方法签名: int whichOne(String[] pastures) (请保证你的方法是public的)
注意:
1.pastures包含的元素的个数在0到50之间
2.每个元素的格式是"a b c d",其中a b c d 是0到1000的整数
3.保证没有2个牧地是相交的
例子:
0):
{"1 3 1 4", "5 7 1 3", "7 8 1 5", "8 10 4 5", "5 6 5 7", "9 10 0 1", "3 5 4 7", "8 10 1 3", "1 3 5 6"}
你的程序必须返回:5
这个例子就代表上图,应用题目所述的规则:
1。3个super pasture分别含有的badness为0,10,5,所有选择第2块super pasture
2。由于卖掉的牧地后不能使该super pasture分裂开,所有不能卖牧地2和7
3。现在在牧地1,3,5种选择,由于5的面积最小为1,所以他最终选择卖5号牧地
1): {"4 6 1 5", "1 6 5 7", "8 13 1 3", "8 10 3 7"}
你的程序必须返回:0
2): {"0 100 0 100", "500 511 500 509", "710 720 710 720"}
你的程序必须返回:1
3): {"0 1 1 2", "0 1 2 3", "0 1 3 4", "0 1 0 1"}
你的程序必须返回2
4): {"0 1 1 2", "0 1 2 3", "0 2 3 4", "0 1 0 1"}
你的程序必须返回3
解题思路:
首先要明确根本不需要将整个地图存起来,因为整个地图大小为1000*1000,内存开销大而且这样做来遍历地图耗时间。由于题目说任意2个牧地没有相交的,我们可以将一个牧地当作一个点来存储,在我的代码中为类Pasture,这样最多只要存储50个就够了。判断2个牧地是否相邻比较简单,直接看我的代码里面的。所以我们很容易建立连接表connect_list。现在的任务是要确认整个地图中有多少个super pasture,并且每个super pasture含有哪些牧地,我们用一个数组block[50]来表示super pasture,block[i]表示第i个牧地,它的数据类型为类Block,其中的成员list包含这个super pasture中的所有的牧地。有了这些,我们就很容易来求了,大家应该知道种子染色法吧,就是在地图中随便丢一种颜色的种子,让它自由扩散到相邻的区域并且给那些区域染相同的颜色,由于已经建立了连接表connect_list,所有用dfs遍历就可以轻松解决。
最后就是求出含有最多badness的集合,然后从这个里面选出合适的牧地来卖。
关于规则中的卖掉一个牧地后不能让该super pasture破裂,这个也很容易实现。假如我们现在要看牧地i是否能买,connect_list[i].list中的全部都是与这个牧地相连的牧地号,所以也属于这个区域,如果这些牧地里面有一个目j,与它相连的牧地数connect_list[j].size为1,既只与i相连,那么把i卖掉后这个区域很定会被破裂,因为j原来属于这个区域,而卖掉i后,j就不属于这个区域了,即分裂了出去。
恩,原理都讲清楚了,下面是我的实现的代码:
1
#include
<
iostream
>
2
#include
<
string
>
3
#include
<
vector
>
4
#include
<
sstream
>
5
using
namespace
std;
6
7
//
表示牧地的类
8
class
Pasture
{
9public:
10 int x1,y1,x2,y2;
11
12 Pasture(int xx1,int yy1,int xx2,int yy2) :x1(xx1),y1(yy1),x2(xx2),y2(yy2) {}
13 int area(){
14 if(x1==x2)
15 return y2-y1+1;
16 if(y1==y2)
17 return x2-x1+1;
18 return (x2-x1+1)*(y2-y1+1);
19 }
20
21 virtual ~Pasture() {}
22}
;
23
24
//
表示区块的类 即SuperPasture,继承自Pasture
25
class
Block :
public
Pasture
{
26 int fill;
27public:
28 vector<int> list; //区块中含有的pasture的序号列表
29
30 Block() : fill(0),Pasture(9999,9999,0,0) {}
31 //向区块中加入序号为index的pasture
32 void set(int index,int xx1,int yy1,int xx2,int yy2){
33 if(x1>xx1) x1=xx1;
34 if(x2<xx2) x2=xx2;
35 if(y1>yy1) y1=yy1;
36 if(y2<yy2) y2=yy2;
37 if(xx1==xx2)
38 fill+=yy2-yy1+1;
39 else if(yy1==yy2)
40 fill+=xx2-xx1+1;
41 else
42 fill+=(xx2-xx1+1)*(yy2-yy1+1);
43 list.push_back(index);
44 }
45 int badness(){
46 return area()-fill;
47 }
48}
;
49
50
//
用来表示pasture连接关系的类
51
class
Connect
{
52public:
53 vector<int> list; //连接表
54 int size(){
55 return list.size();
56 }
57 void push_back(int i){
58 list.push_back(i);
59 }
60}
;
61
62
class
SuperPasture
{
63 vector<Pasture> pasture; //牧地类
64 vector<Connect> connect_list; //连接表
65 Block block[50]; //区块(最多50个)
66 int total_super; //区块的个数
67 bool mark[50]; //用于dfs遍历的标志数组
68
69 bool connect(int,int); //判断2个区块是否相邻
70 void dfs(int,int); //多地图进行dfs遍历
71 bool cansell(int); //判断某个pasture是否可以卖出去
72
73public:
74 SuperPasture(){
75 total_super=0;
76 memset(mark,0,sizeof(mark));
77 }
78 int whichOne(vector<string>);
79}
;
80
81
bool
SuperPasture::connect(
int
i,
int
j)
{
82 bool connect_top_bottom=( abs(pasture[i].x2-pasture[j].x1)==1
|| abs(pasture[i].x1-pasture[j].x2)==1 );
83 bool connect_right_left=( abs(pasture[i].y2-pasture[j].y1)==1
|| abs(pasture[i].y1-pasture[j].y2)==1 );
84 if( connect_top_bottom
&& (pasture[i].y1>=pasture[j].y1&&pasture[i].y1<=pasture[j].y2)
85 || connect_top_bottom
&& (pasture[i].y2>=pasture[j].y1&&pasture[i].y2<=pasture[j].y2)
86 || connect_right_left
&& (pasture[i].x1>=pasture[j].x1&&pasture[i].x1<=pasture[j].x2)
87 || connect_right_left
&& (pasture[i].x2>=pasture[j].x1&&pasture[i].x2<=pasture[j].x2) )
88 return true;
89 return false;
90}
91
92
void
SuperPasture::dfs(
int
i,
int
index)
{
93 if(!mark[i]){
94 mark[i]=true;
95 //向区块index中添加该pasture
96 block[index].set(i,pasture[i].x1,pasture[i].y1,pasture[i].x2,pasture[i].y2);
97 for(int j=0;j<connect_list[i].size();j++)
98 dfs(connect_list[i].list[j],index);
99 }
100}
101
102
bool
SuperPasture::cansell(
int
k)
{
103 for(int i=0;i<connect_list[k].size();i++){
104 int connect_id=connect_list[k].list[i];
105 if(connect_list[connect_id].size()==1)
106 return false;
107 }
108 return true;
109}
110
111
int
SuperPasture::whichOne(vector
<
string
>
pastures)
{
112 int i,j;
113 vector<int> tot_max_badness; //含有badness最大的区块的集合(这样的区块可能有很多)
114
115 //读入数据,初始化pasture和connect_list
116 for(i=0;i<pastures.size();i++){
117 istringstream is(pastures[i].c_str());
118 int a,b,c,d;
119 is>>a>>b>>c>>d;
120 pasture.push_back(Pasture(c,a,d-1,b-1));
121 connect_list.push_back(Connect());
122 }
123
124 //建立连接表
125 for(i=0;i<pasture.size();i++)
126 for(j=0;j<pasture.size();j++)
127 if(i!=j&&(connect(i,j)||connect(j,i)))
128 connect_list[i].push_back(j);
129
130 //进行dfs遍历,并建立block相关信息
131 for(i=0;i<pasture.size();i++)
132 if(!mark[i])
133 dfs(i,++total_super);
134
135 //求出含有badness最多的区块的集合
136 tot_max_badness.push_back(1);
137 for(i=1;i<=total_super;i++){
138 if(block[i].badness() >= block[tot_max_badness.back()].badness()) {
139 if(block[i].badness()>block[tot_max_badness.back()].badness())
140 tot_max_badness.clear();
141 tot_max_badness.push_back(i);
142 }
143 }
144
145 int sell=block[tot_max_badness[0]].list[0];
146 for(i=0;i<tot_max_badness.size();i++){
147 int max_badness=tot_max_badness[i];
148 for(j=0;j<block[max_badness].list.size();j++){
149 int sell_id=block[max_badness].list[j];
150 if(cansell(sell_id)){
151 if(!cansell(sell))
152 sell=sell_id;
153 if(pasture[sell_id].area()<pasture[sell].area())
154 sell=sell_id;
155 if((pasture[sell_id].area()==pasture[sell].area())&&sell_id<sell)
156 sell=sell_id;
157 }
158 }
159 }
160 return sell;
161}
162
测试一样
int main(){
vector<string> p;
p.push_back("0 1 1 2");
p.push_back("0 1 2 3");
p.push_back("0 2 3 4");
p.push_back("0 1 0 1");
SuperPasture s;
cout<<s.whichOne(p);
return 1;
}