USACO Picture 解题报告

本文记录了一位程序员在解决USACO Picture问题时遇到的挑战,从初次尝试到最终采用高人解法的过程。文章讨论了线扫算法的应用,以及在处理矩形重叠情况时的复杂性。作者引用了在线资源和他人解决方案,并指出在处理边的分裂和排序时的微妙之处,揭示了解题中的巧妙构思和技巧。
摘要由CSDN通过智能技术生成

这道题看起来像是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()  






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值