USACO Electric Fenceの解題報告

4746 ワード

この問題はやはり構想がないです.大体調べてみてやっと局部検索だと分かりました.ターゲット関数(各segmentまでの距離の和)は凸関数の和であり、凸関数でもあるので、局部最適解(すなわち、局部最小値)は大域的な最適解です.http://stackoverflow.com/questions/2255889/local-optimums-in-electric-fences.
一つのポイントからsegmentまでの距離はtopcoder tutorial上の方法です.http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=geometry1
大神さんもこの問題を作りました.https://www.byvoid.com/blog/usaco-522-electric-fences)を二回スキャンします.私の乱暴な捜索はもっと早いようです.
USER: chen chen [thestor1]
TASK: fence3
LANG: C++

Compiling...
Compile: OK

Executing...
   Test 1: TEST OK [0.016 secs, 3504 KB]
   Test 2: TEST OK [0.011 secs, 3504 KB]
   Test 3: TEST OK [0.011 secs, 3504 KB]
   Test 4: TEST OK [0.011 secs, 3504 KB]
   Test 5: TEST OK [0.024 secs, 3504 KB]
   Test 6: TEST OK [0.022 secs, 3504 KB]
   Test 7: TEST OK [0.011 secs, 3504 KB]
   Test 8: TEST OK [0.016 secs, 3504 KB]
   Test 9: TEST OK [0.019 secs, 3504 KB]
   Test 10: TEST OK [0.011 secs, 3504 KB]
   Test 11: TEST OK [0.027 secs, 3504 KB]
   Test 12: TEST OK [0.024 secs, 3504 KB]

All tests OK.

YOUR PROGRAM ('fence3') WORKED FIRST TIME! That's fantastic -- and a rare thing. Please accept these special automated congratulations.

/* 
ID: thestor1 
LANG: C++ 
TASK: fence3 
*/
#include <iostream>
#include <fstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <limits>
#include <cassert>
#include <string>
#include <vector>
#include <list>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <algorithm>
#include <cassert>
#include <iomanip>

using namespace std;

const int dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1};

double disttopoint(int x1, int y1, int x2, int y2)
{
	return sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
}

class LineSegment
{
public:
	int x1, y1, x2, y2;
	double dist;
	LineSegment(int x1, int y1, int x2, int y2) : x1(x1), y1(y1), x2(x2), y2(y2) 
	{
		dist = disttopoint(x1, y1, x2, y2);
	}
};

int dot(int x1, int y1, int x2, int y2)
{
	return x1 * x2 + y1 * y2;
}

int cross(int x1, int y1, int x2, int y2)
{
	return x1 * y2 - y1 * x2;
}

double disttosegment(int x, int y, LineSegment &line)
{
	if (dot(line.x2 - line.x1, line.y2 - line.y1, x - line.x2, y - line.y2) >= 0)
	{
		return disttopoint(x, y, line.x2, line.y2);
	}
	if (dot(line.x1 - line.x2, line.y1 - line.y2, x - line.x1, y - line.y1) >= 0)
	{
		return disttopoint(x, y, line.x1, line.y1);
	}

	return abs(cross(line.x2 - line.x1, line.y2 - line.y1, x - line.x1, y - line.y1) * 1.0 / line.dist);
}

double disttolines(int x, int y, std::vector<LineSegment> &linesegments)
{
	double dist = 0;
	for (int i = 0; i < linesegments.size(); ++i)
	{
		dist += disttosegment(x, y, linesegments[i]);
	}
	return dist;
}

void dfs(int x, int y, std::vector<std::vector<bool> > &visited, std::vector<LineSegment> &linesegments, int &bestx, int &besty, double &bestdist)
{
	visited[x][y] = true;

	int nx, ny;
	double dist;
	
	for (int d = 0; d < 4; ++d)
	{
		nx = x + dx[d], ny = y + dy[d];
		if (nx < 0 || nx > 1000 || ny < 0 || ny > 1000 || visited[nx][ny])
		{
			continue;
		}
		dist = disttolines(nx, ny, linesegments);
		if (dist <= bestdist)
		{
			bestdist = dist;
			bestx = nx, besty = ny;
			dfs(nx, ny, visited, linesegments, bestx, besty, bestdist);
		}
	}
}

int main()
{
	ifstream fin("fence3.in");
	int F;
	fin >> F;

	std::vector<LineSegment> linesegments;
	int x1, y1, x2, y2;
	for (int i = 0; i < F; ++i)
	{
		fin >> x1 >> y1 >> x2 >> y2;
		linesegments.push_back(LineSegment(x1 * 10, y1 * 10, x2 * 10, y2 * 10));
	}
	fin.close();

	// cout << "linesegments:" << endl;
	// for (int i = 0; i < linesegments.size(); ++i)
	// {
	// 	cout << i << ": " << linesegments[i].x1 << ", " << linesegments[i].y1 << ", " << linesegments[i].x2 << ", " << linesegments[i].y2 << "[" << linesegments[i].dist << "]" << endl;
	// }
	
	std::vector<std::vector<bool> > visited(1001, std::vector<bool>(1001, false));
	int bestx = 0, besty = 0;
	double bestdist = disttolines(0, 0, linesegments);
	dfs(0, 0, visited, linesegments, bestx, besty, bestdist);

	ofstream fout("fence3.out");
	fout << fixed << setprecision(1);
	fout << bestx / 10.0 << " " << besty / 10.0 << " " << bestdist / 10.0 << endl;
	fout.close();
	return 0;  
}