1294-Zigzag

問題概要

いくつかの点が与えられる.
その点を全て通るような折れ線を求めよ.
ただし,折れ曲る回数が出来るだけ少なく,かつ長さの合計が出来るだけ少なくなるようにせよ.
また折れ線に含まれる各線分はかならず与えられた点を2つより多く通らないといけないものとする.

解法

2つの点を通る全ての直線をリストアップし,その任意の2つの交点を全て求める.
後は点について最良優先探索(ダイクストラ)する.

実装(C++)

#include <algorithm>
#include <vector>
#include <iostream>
#include <complex>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <queue>
#include <cstdlib>
using namespace std;

typedef long long int lli;

#define REP(i,x) for(int i=0;i<(int)(x);i++)
#define rep(i,s,x) for(int i=s;i<(int)(x);i++)
#define FOR(i,c) for(__typeof((c).begin())i=(c).begin();i!=(c).end();i++)
#define RREP(i,x) for(int i=(x);i>=0;i--)
#define RFOR(i,c) for(__typeof((c).rbegin())i=(c).rbegin();i!=(c).rend();i++)
const double EPS=1e-4;
typedef complex<double> Point;
inline double size(const Point &a){return abs(a);}
inline double cross(const Point &a,const Point &b){return imag(conj(a)*b);}
inline double dot(const Point &a,const Point &b){return real(conj(a)*b);}
inline double atan(const Point &a){return atan2(imag(a),real(a));}

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);}
}
struct Line:vector<Point>{
	Line(Point a,Point b){
		(*this).push_back(a);(*this).push_back(b);
	}
};
int ccw(Point a,Point b,Point c){
	b-=a;c-=a;
	if(cross(b, c)>0)return +1;       // counter clockwise
	if(cross(b, c)<0)return -1;       // clockwise
	if(dot(b, c)<0)return +2;       // c--a--b on line
	if(norm(b)<norm(c))return -2;       // a--b--c on line
	return 0;
}
bool is_intersect_LL(const Line &l, const Line &m) {
	return abs(cross(l[1]-l[0], m[1]-m[0])) > EPS || // non-parallel
			abs(cross(l[1]-l[0], m[0]-l[0])) < EPS;   // same line
}
bool is_intersect_SP(const Line &s, const Point &p) {
	return abs(s[0]-p)+abs(s[1]-p)-abs(s[1]-s[0]) < 1e-8; // triangle inequality
}
Point crosspoint_LL(const Line &l, const Line &m) {
	double A = cross(l[1] - l[0], m[1] - m[0]);
	double B = cross(l[1] - l[0], l[1] - m[0]);
	if (abs(A) < EPS && abs(B) < EPS) return m[0]; // same line
	return m[0] + B / A * (m[1] - m[0]);
}
ostream &operator<<(ostream &os,const Point &a){
	os<<"("<<real(a)<<","<<imag(a)<<")";
	return os;
}
int n;
vector<Point> points;
vector<Point> pts;
struct State{
	int count;
	double ans;
	int used;
	int now;
};
bool operator<(const State &a,const State &b){
	return a.count!=b.count?a.count<b.count:a.ans<b.ans;
}
bool operator>(const State &b,const State &a){return a<b;}
#define MAXpts 1200
int data[MAXpts][MAXpts];
char ac[6][1<<10][MAXpts];
vector<int> next[MAXpts];
void solve(){
	priority_queue<State,vector<State>,greater<State> > que;
	REP(i,n)que.push((State){-1,0,0,i});
	memset(ac,0,sizeof(ac));
	while(!que.empty()){
		State now=que.top();que.pop();
		if(ac[now.count+1][now.used][now.now])continue;
		ac[now.count+1][now.used][now.now]=1;
		if(now.used==(1<<n)-1){
			cout<<now.count<<" "<<setprecision(5)<<setiosflags(ios::fixed)<<now.ans<<endl;
			return;
		}
		if(now.count==4)continue;
		REP(i,next[now.now].size()){
			//次の場所を探す
			if((now.used|data[now.now][next[now.now][i]])!=now.used){
				que.push((State){now.count+1,now.ans+abs(pts[now.now]-pts[next[now.now][i]]),now.used|data[now.now][next[now.now][i]],next[now.now][i]});
			}
		}
	}
	cerr<<"No Answer Error"<<endl;
}
int main(){
	for(;cin>>n,n;){
		points=vector<Point>(n);
		REP(i,n){
			int x,y;
			cin>>x>>y;
			points[i].real()=x;
			points[i].imag()=y;
		}
		vector<Line> lines;
		REP(i,n){
			rep(j,i+1,n){
				lines.push_back(Line(points[i],points[j]));
			}
		}
		pts.clear();
		REP(i,n){
			pts.push_back(points[i]);
		}
		REP(i,lines.size()){
			rep(j,i+1,lines.size()){
				if(is_intersect_LL(lines[i],lines[j])){
					Point ne=crosspoint_LL(lines[i],lines[j]);
					bool ok=true;
					REP(k,pts.size()){
						if(pts[k]==ne)ok=false;
					}
					if(ok)pts.push_back(ne);
				}
			}
		}
		REP(i,pts.size()){
			rep(j,i,pts.size()){
				data[i][j]=0;
				Line t=Line(pts[i],pts[j]);
				REP(k,n){
					if(is_intersect_SP(t,points[k])){
						data[i][j]|=1<<k;
					}
				}
				if(__builtin_popcount(data[i][j])<=1){
					data[i][j]=0;
				}
				data[j][i]=data[i][j];
			}
		}
		REP(i,pts.size()){
			next[i].clear();
			REP(j,pts.size()){
				if(data[i][j]){
					next[i].push_back(j);
				}
			}
		}
		solve();
	}
}