1272-Polygons on the Grid

問題概要

辺の流さが与えられた数値で表わせる,頂点が格子点上にある凸型多角形の面積の最大を求めよ.

解法

枝刈り全探索.
辺の使い方(1 2 4 5を3 4 5や5 5 2にするなど)をまず全探索する.
それぞれの辺の使い方に対し,辺の並び方をnext_permutationなどで決める.
凸にならないようなものや,遠すぎるようなものを枝刈りして,最大の面積を求める.

実装(C++)

#include <iostream>
#include <complex>
#include <algorithm>
#include <vector>
#include <cmath>
#include <numeric>
#include <ctime>
#include <cstdio>
#include <set>
using namespace std;

const double EPS=1e-7;
const double INF=1e10;
#define REP(i,x) for(int i=0;i<(int)x;i++)
#define FOR(i,c) for(__typeof((c).begin())i=(c).begin();i!=(c).end();i++)
typedef complex<int> Point;
ostream &operator<<(ostream &os,const Point &a){
	return os<<"("<<real(a)<<","<<imag(a)<<")";
}
inline int cross(const Point &a,const Point &b){return imag(conj(a)*b);}
inline int dot(const Point &a,const Point &b){return real(conj(a)*b);}
namespace std{
bool operator<(const Point &a,const Point &b){return real(a)==real(b)?imag(a)<imag(b):real(a)<real(b);}
Point operator*(const Point &a,const double &b){return Point(real(a)*b,imag(a)*b);}
}
int ccw(Point a,Point b,Point c){
	b-=a;c-=a;
	if(cross(b, c)>0)return +1;       // counter clockwise
	if(norm(b)<norm(c))return -2;       // a--b--c on line
	return 0;
}
#define curr(P, i) P[(i) % n]
#define next(P, i) P[(i+1) % n]
#define prev(P, i) P[(i+n-1) % n]

typedef pair<int,int> Q;
vector<Point> circle[1801];

int n;
int r[6];
Point P[6];
int Sum;
int area(int m){
	int A=0;
	for(int i=0;i<m;++i)
		A+=cross(curr(P, i),next(P, i));
	return A/2;
}
int debug=0;
int answer=-1;
void solve(int k){
	if(k>1)Sum-=r[k-2];
	debug++;
	if(k>2){
		int t=ccw(P[k-3],P[k-2],P[k-1]);
		if(t!=1){
			if(k>1)Sum+=r[k-2];
			return;
		}
	}
	if(k>1&&P[k-1].real()==0&&P[k-1].imag()==0){
		if(k>1)Sum+=r[k-2];
		return;
	}
	if(P[k-1].imag()<0){
		if(k>1)Sum+=r[k-2];
		return;
	}
	if(norm(P[k-1]-P[0])>Sum*Sum){
		if(k>1)Sum+=r[k-2];
		return;
	}
	if(k==n){
		//解があるかどうか
		for(int i=0;i<(int)circle[r[k-1]].size();i++){
			if(abs(P[k-1].real())==abs(circle[r[k-1]][i].real())&&abs(P[k-1].imag())==abs(circle[r[k-1]][i].imag())){
				answer=max(answer,area(n));
				break;
			}
		}
	}else if(k==1){
		//辺を適当に置く(45度の範囲)
		for(int i=0;i<(int)circle[r[k-1]].size()/2;i++){
			P[k]=circle[r[k-1]][i];
			solve(k+1);
		}
	}else{
		//辺を適当に置く
		for(int i=0;i<(int)circle[r[k-1]].size();i++){
			P[k].real()=circle[r[k-1]][i].real()+P[k-1].real();
			P[k].imag()=circle[r[k-1]][i].imag()+P[k-1].imag();
			solve(k+1);

			P[k].real()=-circle[r[k-1]][i].real()+P[k-1].real();
			P[k].imag()=circle[r[k-1]][i].imag()+P[k-1].imag();
			if(circle[r[k-1]][i].real())solve(k+1);

			P[k].real()=circle[r[k-1]][i].real()+P[k-1].real();
			P[k].imag()=-circle[r[k-1]][i].imag()+P[k-1].imag();
			if(circle[r[k-1]][i].imag())solve(k+1);

			P[k].real()=-circle[r[k-1]][i].real()+P[k-1].real();
			P[k].imag()=-circle[r[k-1]][i].imag()+P[k-1].imag();
			solve(k+1);

			P[k]=P[0];
		}
	}
	if(k>1)Sum+=r[k-2];
	return;
}

void solve(){
	int min_c=0;
	sort(r,r+n,greater<int>());
	REP(i,n){
		if(circle[r[min_c]].size()>circle[r[i]].size()){
			min_c=i;
		}
	}
	swap(r[0],r[min_c]);
	sort(r+1,r+n,greater<int>());
	Sum=accumulate(r,r+n,0);
	do{
		P[0]=Point(0,0);
		debug=0;
		solve(1);
	}while(next_permutation(r+1,r+n,greater<int>()));
}
set<multiset<int> > ac;
void dfs(multiset<int> r_data){
	if(r_data.size()<3)return;
	if(ac.find(r_data)==ac.end()){
		ac.insert(r_data);
		n=r_data.size();
		int k=0;
		int t[6];
		FOR(it,r_data){
			t[k]=*it;
			r[k++]=*it;
		}
		n=k;
		solve();
		for(int i=0;i<r_data.size();i++){
			for(int j=i+1;j<r_data.size();j++){
				multiset<int> next;
				next.insert(t[i]+t[j]);
				for(int k=0;k<r_data.size();k++){
					if(k==i||k==j)continue;
					next.insert(t[k]);
				}
				dfs(next);
			}
		}
	}
}
int main() {
	for(int i=1;i<=1800;i++){
		for(int j=0;j<=i;j++){
			int t=sqrt(i*i-j*j)+1e-8;
			if(t*t+j*j==i*i){
				circle[i].push_back(Point(t,j));
			}
		}
	}
	for(;cin>>n,n;){
		REP(i,n)cin>>r[i];
		answer=-1;
		ac.clear();
		multiset<int> v;
		REP(i,n)v.insert(r[i]);
		dfs(v);
		cout<<answer<<endl;
	}
	return 0;
}