poj 1514&zoj 1185 Metal Cutting(半平面交)

Metal Cutting
Time Limit: 10000MS Memory Limit: 10000K
Total Submissions: 403 Accepted: 172
Description

In order to build a ship to travel to Eindhoven, The Netherlands, various sheet metal parts have to be cut from rectangular pieces of sheet metal. Each part is a convex polygon with at most 8 vertices. Each rectangular piece of sheet metal has width n and height m, so that the four corners of the sheet can be specified by the Cartesian coordinates (0, 0), (0, m), (n, m) and (n, 0) in clockwise order. The cutting machine available can make only straight-line cuts completely through the metal. That is, it cannot cut halfway through the sheet, turn, and then cut some more. You are asked to write a program to determine the minimum total length of cuts this machine has to make in order to cut out the polygon. The cuts must be along the edges of the poligon.

For example, if n = m = 100, and the polygon has vertices (80, 80), (70, 30), (20, 20) and (20, 80), the following diagram shows the optimal cut (the thick lines). The numbers show the order in which the cuts are made.


Input

The first line of input contains the two integers n and m where 0 < n, m <= 500. The next line contains p, the number of vertices in the polygon, where 3 <= p <= 8. Each of the next p lines contains two integers x and y where 0 < x < n and 0 < y < m, specifying the vertices of the polygon. The vertices are listed in clockwise order. You may assume that the polygon does not intersect itself, and that no three consecutive vertices are colinear.
Output

Print the minimum total length of cuts required to cut out the given polygon, accurate to 3 decimal places.
Sample Input

100 100
4
80 80
70 30
20 20
20 80
Sample Output

Minimum total length = 312.575
Source

East Central North America 1998


题目大意:

一张正方形纸上有p个凸包的顶点。每次选择凸包的一条边沿着这条边将纸成两块,直到把纸剪成凸包。问按照怎样的顺序剪才能使剪的长度最短。

思路:

这题第一眼的做法应该是暴力枚举剪的顺序, 然后用半平面交模拟剪纸的过程。

其实还有更简单的做法,对每一次剪纸的长度只与当前边的左右的边是否剪过有关,只要处理出当前边的左右是否被剪过,就可以通过枚举或者状压DP求出答案,详情百度狗狗40题参照watashi博客。

不过作为弱逼还是只想到了做半平面交,于是写了一个N^2的半平面交。


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <ctime>
#include <bitset>
#include <algorithm>
#include <set>
#include <string>
#include <vector>
#include <map>
#include <list>
#include <stack>
#include <queue>
#include <deque>

#define LL long long
#define PC polygon_convex

using namespace std;

const double eps = 1e-8;
const double pi = acos(-1.0);

int cmp(double x) {
    if(fabs(x) < eps) return 0;
    if(x > 0)return 1;
    return -1;
}

inline double sqr(double x) {
    return x * x;
}

struct point {
    double x, y;
    point(){}
    point(double a, double b): x(a), y(b) {}
    void input(){
        scanf("%lf%lf",&x,&y);
    }

    friend point operator + (const point &a, const point &b) {
        return point(a.x + b.x, a.y + b.y);
    }

    friend point operator - (const point &a, const point &b) {
        return point(a.x - b.x, a.y - b.y);
    }

    friend bool operator == (const point &a, const point &b) {
        return cmp(a.x - b.x) == 0 && cmp(a.y - b.y) == 0;
    }

    friend point operator * (const point &a, const double &b) {
        return point(a.x * b, a.y * b);
    }

    friend point operator * (const double &a, const point &b) {
        return point(a * b.x, a * b.y);
    }

    friend point operator / (const point &a, const double &b) {
        return point(a.x / b, a.y / b);
    }
    double norm(){
        return sqrt(sqr(x) + sqr(y));
    }
};

double det(const point &a, const point &b) {
    return a.x * b.y - a.y * b.x;
}

double dot(const point &a, const point &b) {
    return a.x * b.x + a.y * b.y;
}

double dist(const point &a, const point &b) {
    return (a - b).norm();
}

double Angle(point a, point b) {
	if(cmp(dot(a, b) - a.norm() * b.norm()) == 0) return 0;
	return acos(dot(a,b) / a.norm() / b.norm());
}

point rotate_point(const point &p, double A){
    double tx = p.x, ty = p.y;
    return point(tx * cos(A) - ty * sin(A), tx * sin(A) + ty * cos(A));
}

struct line {
    point a, b;
    line(){}
    line(point x, point y):a(x),b(y){}
};
line point_make_line(const point a, const point b) {
    return line(a,b);
}

double dis_point_segment(const point p, const point s, const point t) {
    if(cmp(dot(p - s, t - s))<0) return (p-s).norm();
    if(cmp(dot(p - t, s - t))<0) return (p-t).norm();
    return fabs(det(s - p, t - p) / dist(s, t));
}

void PointProjLine(const point p, const point s, const point t, point &cp) {
    double r = dot(t - s, p - s) / dot (t - s, t - s);
    cp = s + r * (t - s);
}

bool PointOnSegment(point p, point s, point t) {
    return cmp(det(p - s, t - s))==0 && cmp(dot(p - s, p - t)) <= 0;
}

bool parallel(line a, line b) {
    return !cmp(det(a.a - a.b, b.a - b.b));
}

bool line_make_point(line a, line b, point &res){
    if(parallel(a, b)) return false;
    double s1 = det(a.a - b.a, b.b - b.a);
    double s2 = det(a.b - b.a, b.b - b.a);
    res = (s1 * a.b - s2 * a.a) / (s1 - s2);
    return true;
}

const int maxn = 100;

struct polygon {
    int n;
    point a[maxn];
    polygon() {}
    double perimeter() {
        double sum = 0;
        a[n] = a[0];
        for (int i = 0; i < n; i++) sum += (a[i+1] - a[i]).norm();
        return sum;
    }

    double area() {
        double sum = 0;
        a[n] = a[0];
        for (int i = 0; i < n; i++) sum += det(a[i+1], a[i]);
        return sum/2.;
    }

    int Point_In(point t){
        int num = 0, i, d1, d2, k;
        a[n] = a[0];
        for(i = 0; i < n; i++){
            if(PointOnSegment(t, a[i], a[i+1])) return 2;
            k = cmp(det(a[i+1] - a[i], t - a[i]));
            d1 = cmp(a[i].y - t.y);
            d2 = cmp(a[i+1].y - t.y);
            if( k > 0 && d1 <= 0 && d2 > 0) num++;
            if( k < 0 && d2 <= 0 && d1 > 0) num--;
        }
        return num != 0;
    }
	point MassCenter() {
    		point ans = point(0, 0);
    		if(cmp(area()) == 0) return ans;
   		a[n] = a[0];
    		for (int i = 0; i < n; i++) ans = ans + (a[i] + a[i + 1]) * det(a[i + 1], a[i]);
    		return ans / area() / 6.;
	}
};




struct polygon_convex {
    vector<point> P;
    polygon_convex(int Size = 0) {
        P.resize(Size);
    }
    double perimeter() {
        double ret = 0;
        int n = P.size();
        #define next(i) ( (i + 1) % n)
        for(int i = 0; i < n; i++) ret += (P[i] - P[next(i)]).norm();
        return ret;
    }

    double area() {
        double ret = 0;
        int n = P.size();
        #define next(i) ( (i + 1) % n)
        for(int i = 0 ;i < n; i++)ret += det(P[next(i)], P[i]);
        return ret / 2;
    }
};

bool comp_less(const point &a, const point &b){
    return cmp(a.x -b.x) < 0 || cmp(a.x - b.x) == 0 && cmp (a.y - b.y) < 0;
}

polygon_convex convex_hull(vector<point> a) {
    polygon_convex res(2 * a.size() + 5);
    sort(a.begin(), a.end(), comp_less);
    a.erase(unique(a.begin(), a.end()), a.end());
    int m = 0;
    for(int i = 0; i < a.size(); i++){
        while( m > 1 && cmp(det(res.P[m-1] - res.P[m-2], a[i] - res.P[m - 2])) <= 0) m--;
        res.P[m++] = a[i];
    }

    int k = m;
    for(int i = int(a.size()) - 2; i >= 0; i--) {
        while(m > k && cmp(det(res.P[m-1] - res.P[m-2], a[i] - res.P[m-2])) <= 0) m--;
        res.P[m++] = a[i];
    }
    res.P.resize(m);
    if(a.size() > 1) res.P.resize(m-1);
    return res;
}

vector<point> p;
int a[10], N, M, n;
PC pc;
double ans;
bool flag[10];
PC new_pol, pol;
point A, B, C, D;
vector<point> cut;

double calc() {
	double ret = 0;
	p.clear();
	p.push_back(point(0, 0));
	p.push_back(point(N, 0));
	p.push_back(point(N, M));	
	p.push_back(point(0, M));
	pol = convex_hull(p);
	int m = pc.P.size();
	for(int i = 0; i < m; i++) {
		A = pc.P[a[i]];
		B = pc.P[(a[i] + 1) % m];
		new_pol.P.clear();
		cut.clear();
		for(int j = 0; j < (int)pol.P.size(); j++) {
			C = pol.P[j];
			D = pol.P[(j + 1) % pol.P.size()];
			if(cmp(det(B - A, C - A)) >= 0) new_pol.P.push_back(C);
			if(cmp(det(B - A, C - D)) != 0) {
				point ip;	
				if(line_make_point(line(A, B), line(C, D), ip) && PointOnSegment(ip, C, D)) {
					new_pol.P.push_back(ip);
					cut.push_back(ip);
				}
			}		
		}
    	sort(cut.begin(), cut.end(), comp_less);
    	cut.erase(unique(cut.begin(), cut.end()), cut.end());
    	ret += (cut[0] - cut[1]).norm();
    	pol = new_pol;
	}
	return ret;
}

void search(int x) {
	if(x >= pc.P.size()) {
		double t = calc();
		ans = min(ans, t);
		return;
	}

	for (int i = 0; i < (int)pc.P.size(); i++) if(!flag[i]) {
		a[x] = i;
		flag[i] = true;
		search(x + 1);
		flag[i] = false;
	}
}

int main() {
	int T;
	scanf("%d", &T);	
	while(T--) {
		//init();
		scanf("%d%d", &N, &M);
		scanf("%d", &n);
		p.resize(n);
		for(int i = 0; i < n; i++) p[i].input(); 
		pc = convex_hull(p);
		ans = 1234567890;
		search(0);
		printf("Minimum total length = %.3f\n", ans);
		if(T) puts("");
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值