这道题看起来像是line sweep的应用。我看了topcoder上的tutorial: http://apps.topcoder.com/forums/?module=Thread&threadID=684537. 然后有了下面的实现。但是,无论是naive的实现(即测试一下当前所有的active的矩形有没有遮挡)或是稍稍改进的实现(按照上边缘排序,从下边缘大于当前的矩形的上边缘的矩形开始测试),都没法过最后一个测试点(测试点11)。所以下面的实现只能过10个测试点:
/*
ID: thestor1
LANG: C++
TASK: picture
*/
#include <iostream>
#include <fstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <climits>
#include <cassert>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <algorithm>
#include <cassert>
using namespace std;
class Rectangle
{
public:
int lx, ly, ux, uy;
Rectangle(int lx, int ly, int ux, int uy)
{
this->lx = lx;
this->ly = ly;
this->ux = ux;
this->uy = uy;
}
};
enum event_type
{
// EVENT_TOP,
// EVENT_BOTTOM,
EVENT_START,
EVENT_END // ordered this way so that events with same x sort this way
};
class Event
{
public:
int type, x, rectangleid;
Event() {}
Event(int type, int x, int rectangleid) : type(type), x(x), rectangleid(rectangleid) {}
// sort by x then by type
bool operator <(const Event &e) const
{
if (x != e.x)
{
return x < e.x;
}
else
{
return type < e.type;
}
}
};
int linesweep(const vector<Rectangle> &rectangles)
{
vector<Event> events;
int lx, ly, ux, uy;
for (int i = 0; i < rectangles.size(); ++i)
{
lx = rectangles[i].lx, ux = rectangles[i].ux;
events.push_back(Event(EVENT_START, lx, i)); // rectangle start
events.push_back(Event(EVENT_END, ux, i)); // rectangle end
// cout << "[debug]events.push_back:"<<"Event(0, " << lx<<", "<<i<<")"<<endl;
// cout << "[debug]events.push_back:"<<"Event(1, " << ux<<", "<<i<<")"<<endl;
}
sort(events.begin(), events.end());
// cout << "[debug]events:" << endl;
// for (int i = 0; i < events.size(); ++i)
// {
// cout << events[i].type << ", " << events[i].x << ", " << events[i].rectangleid << endl;
// }
set<pair<int, int> > active;
int perimeter = 0;
queue<pair<int, int> > segments;
for (int i = 0; i < events.size(); ++i)
{
// cout << i << ": " << events[i].type << ", " << events[i].x << ", " << events[i].rectangleid << endl;
if (events[i].type == EVENT_END)
{
active.erase(make_pair(rectangles[events[i].rectangleid].uy, events[i].rectangleid));
}
int rly = rectangles[events[i].rectangleid].ly, ruy = rectangles[events[i].rectangleid].uy;
segments.push(make_pair(rly, ruy));
set<pair<int, int> >::const_iterator begin = lower_bound(active.begin(), active.end(), make_pair(rly, events[i].rectangleid));
for (set<pair<int, int> >::const_iterator iter = begin; iter != active.end(); ++iter)
// for (set<pair<int, int> >::const_iterator iter = active.begin(); iter != active.end(); ++iter)
{
// calculate boundary
if (rectangles[iter->second].ly > ruy || rectangles[iter->second].uy < rly)
{
continue;
}
int qsize = segments.size();
if (qsize == 0)
{
break;
}
for (int j = 0; j < qsize; ++j)
{
ly = segments.front().first, uy = segments.front().second;
segments.pop();
if (rectangles[iter->second].ly > uy || rectangles[iter->second].uy < ly)
{
segments.push(make_pair(ly, uy));
continue;
}
int oly = max(ly, rectangles[iter->second].ly);
int ouy = min(uy, rectangles[iter->second].uy);
if (ly < oly)
{
segments.push(make_pair(ly, oly));
}
if (ouy < uy)
{
segments.push(make_pair(ouy, uy));
}
}
}
while (!segments.empty())
{
ly = segments.front().first, uy = segments.front().second;
assert(uy > ly);
// cout << "[debug]added: ly:" << ly << ", uy:" << uy <<endl;
perimeter += uy - ly;
segments.pop();
}
if (events[i].type == EVENT_START)
{
active.insert(make_pair(rectangles[events[i].rectangleid].uy, events[i].rectangleid));
}
}
return perimeter;
}
void swap(int &a, int &b)
{
int tmp = a;
a = b;
b = tmp;
}
int main()
{
ifstream fin("picture.in");
ofstream fout("picture.out");
int N;
fin>>N;
vector<Rectangle> rectangles;
int lx, ly, ux, uy;
for (int i = 0; i < N; ++i)
{
fin>>lx>>ly>>ux>>uy;
rectangles.push_back(Rectangle(lx, ly, ux, uy));
}
int perimeter = linesweep(rectangles);
for (int i = 0; i < rectangles.size(); ++i)
{
swap(rectangles[i].lx, rectangles[i].ly);
swap(rectangles[i].ux, rectangles[i].uy);
}
perimeter += linesweep(rectangles);
fout << perimeter << endl;
fin.close();
fout.close();
return 0;
}
USER: chen chen [thestor1] TASK: picture LANG: C++ Compiling... Compile: OK Executing... Test 1: TEST OK [0.005 secs, 3508 KB] Test 2: TEST OK [0.005 secs, 3508 KB] Test 3: TEST OK [0.008 secs, 3508 KB] Test 4: TEST OK [0.005 secs, 3508 KB] Test 5: TEST OK [0.005 secs, 3508 KB] Test 6: TEST OK [0.014 secs, 3508 KB] Test 7: TEST OK [0.095 secs, 3644 KB] Test 8: TEST OK [0.003 secs, 3508 KB] Test 9: TEST OK [0.084 secs, 3644 KB] Test 10: TEST OK [0.008 secs, 3508 KB] Test 11: TEST OK [0.443 secs, 3508 KB] All tests OK.Your program ('picture') produced all correct answers! This is your submission #8 for this problem. Congratulations!
这道题终于过了。最后还是用的大神的解法:https://www.byvoid.com/blog/usaco-551-picture。这构思过于巧妙,我竟无言以对。粗略地看了大神的思路,然后自己实现。结果发现在有边重叠的情况处理不了。代码中注释的那一段恶心的代码是为了处理这种情况,基本上就是在这种情况下消去重叠的部分,但是发现有的情况是需要算一遍的。然后又去看大神的描述,大神的方法是把起始边放在终边的前面,这样能真正判断到底是不是一条需要算的边。实在是太巧妙了。另外,由于边需要分裂,之前的border(就是大神代码中得level)也需要分裂,我这里一个用的list,另一个用的queue,不可避免地需要入队出队等操作,实际上比大神的简单不易错的方法还慢。差距太大了。
/*
ID: thestor1
LANG: C++
TASK: picture
*/
#include <iostream>
#include <fstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <climits>
#include <cassert>
#include <string>
#include <vector>
#include <list>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <algorithm>
#include <cassert>
using namespace std;
class Rectangle
{
public:
int lx, ly, ux, uy;
Rectangle(int lx, int ly, int ux, int uy)
{
this->lx = lx;
this->ly = ly;
this->ux = ux;
this->uy = uy;
}
};
class Line
{
public:
int start, end, position;
bool bottom;
Line(int start, int end, int position, bool bottom) : start(start), end(end), position(position), bottom(bottom) {}
};
class Border
{
public:
int start, end, layer;
Border(int start, int end, int layer) : start(start), end(end), layer(layer) {}
};
bool by_position(const Line &l1, const Line &l2)
{
if (l1.position != l2.position)
{
return l1.position < l2.position;
}
else
{
return l1.bottom > l2.bottom;
}
}
int layers(vector<Line> &lines)
{
sort(lines.begin(), lines.end(), by_position);
queue<Border> borders;
borders.push(Border(-10000, 10000, 0));
int perimeter = 0;
list<Line> segments;
for (int i = 0; i < lines.size(); ++i)
{
// cout << "[debug]lines[" << i << "]: " << lines[i].start << ", " << lines[i].end << ", " << lines[i].position << endl;
// bool atborder = false;
// if (lines[i].position != lines[0].position && lines[i].position != lines[lines.size() - 1].position)
// {
// if (lines[i].position == lines[i + 1].position && !(lines[i].start >= lines[i + 1].end || lines[i].end <= lines[i + 1].start))
// {
// int ostart = max(lines[i].start, lines[i + 1].start);
// int oend = min(lines[i].end, lines[i + 1].end);
// if (lines[i].start < ostart)
// {
// segments.push_back(Line(lines[i].start, ostart, lines[i].position, lines[i].bottom));
// }
// if (oend < lines[i].end)
// {
// segments.push_back(Line(oend, lines[i].end, lines[i].position, lines[i].bottom));
// }
// }
// else
// {
// segments.push_back(lines[i]);
// }
// if (lines[i].position == lines[i - 1].position && !(lines[i].start >= lines[i - 1].end || lines[i].end <= lines[i - 1].start))
// {
// list<Line> osegments = segments;
// segments.clear();
// for (list<Line>::iterator iter = osegments.begin(); iter != osegments.end(); ++iter)
// {
// Line segment = *iter;
// int ostart = max(segment.start, lines[i - 1].start);
// int oend = min(segment.end, lines[i - 1].end);
// // iter = segments.erase(iter);
// if (segment.start < ostart)
// {
// segments.push_front(Line(segment.start, ostart, lines[i].position, lines[i].bottom));
// }
// if (oend < segment.end)
// {
// segments.push_front(Line(oend, segment.end, lines[i].position, lines[i].bottom));
// }
// }
// }
// }
// else
// {
// atborder = true;
segments.push_back(lines[i]);
// }
int qsize = borders.size();
for (int j = 0; j < qsize; ++j)
{
if (segments.size() == 0)
{
break;
}
Border border = borders.front();
borders.pop();
// cout << "[debug]border:" << border.start << ", " << border.end << ", " << border.layer << endl;
if (border.start >= lines[i].end || border.end <= lines[i].start)
{
borders.push(border);
continue;
}
// bool overlap = false;
for (list<Line>::iterator iter = segments.begin(); iter != segments.end(); ++iter)
{
Line segment = *iter;
// cout << "segment:" << segment.start << ", " << segment.end << ", " << segment.position << endl;
if (border.start >= segment.end || border.end <= segment.start)
{
continue;
}
// overlap = true;
int ostart = max(border.start, segment.start);
int oend = min(border.end, segment.end);
if ((lines[i].bottom && border.layer == 0) || (!lines[i].bottom && border.layer == 1))
{
// cout << "[debug]added:" << ostart << ", " << oend << endl;
perimeter += oend - ostart;
}
if (border.start < ostart)
{
borders.push(Border(border.start, ostart, border.layer));
}
if (lines[i].bottom)
{
borders.push(Border(ostart, oend, border.layer + 1));
}
else
{
borders.push(Border(ostart, oend, border.layer - 1));
}
if (oend < border.end)
{
borders.push(Border(oend, border.end, border.layer));
}
segments.erase(iter);
if (segment.start < ostart)
{
segments.push_back(Line(segment.start, ostart, segment.position, lines[i].bottom));
}
if (oend < segment.end)
{
segments.push_back(Line(oend, segment.end, segment.position, lines[i].bottom));
}
break;
}
// if (!overlap)
// {
// cout << "[debug]called!!!!!!!!!!!!!!!!!!!!!!!!!" << endl;
// borders.push(border);
// }
}
assert(segments.size() == 0);
}
return perimeter;
}
int main()
{
ifstream fin("picture.in");
int N;
fin>>N;
vector<Rectangle> rectangles;
int lx, ly, ux, uy;
for (int i = 0; i < N; ++i)
{
fin>>lx>>ly>>ux>>uy;
rectangles.push_back(Rectangle(lx, ly, ux, uy));
}
fin.close();
vector<Line> lines;
for (int i = 0; i < rectangles.size(); ++i)
{
lines.push_back(Line(rectangles[i].lx, rectangles[i].ux, rectangles[i].ly, true));
lines.push_back(Line(rectangles[i].lx, rectangles[i].ux, rectangles[i].uy, false));
}
int perimeter = layers(lines);
lines.clear();
for (int i = 0; i < rectangles.size(); ++i)
{
lines.push_back(Line(rectangles[i].ly, rectangles[i].uy, rectangles[i].lx, true));
lines.push_back(Line(rectangles[i].ly, rectangles[i].uy, rectangles[i].ux, false));
}
perimeter += layers(lines);
ofstream fout("picture.out");
fout << perimeter << endl;
fout.close();
return 0;
}
python画图的程序放在后面:
import matplotlib.pyplot as plt
with open("picture.in") as f:
n = int(f.readline())
for i in range(n):
x1, y1, x2, y2 = [int(x) for x in f.readline().split()]
plt.plot([x1, x2, x2, x1, x1], [y1, y1, y2, y2, y1], '-')
plt.show()