uva 10641 - Barisal Stadium

动态规划,同时又涉及到几何的一些知识。首先对于输入的那些顶点数据求出对应的多边形的中心,然后利用叉乘的符号判断对应的光源的点是否能够照射到对应的边,判断完所有的边之后就记录该光源点能够照射的范围,这里出现了一个技巧在于由于这个多边形是环状的,但是我们在处理环形的时候是把多边形的边数变为原来的两倍,也就是说,多边形的边的实际编号为0到N-1,从后面开始,N就代表着第0条边,N+1就代表着第1条边,以此类推,这样做是为了便于后面动态规划的处理。

在上面的处理全部都结束的时候,我们分别从第1条边一直到第N条边开始进行动态规划的操作,从当前的边i开始,偏移j作为起始边,判断是否能够有光源照到当前的起始边,如果可以就对最后能照到的那条边的代价之和进行更新,并且要记录所有情况的最优值,具体实现见如下代码:

#include<iostream>
#include<vector>
#include<string>
#include<set>
#include<stack>
#include<queue>
#include<map>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<cstring>
#include<sstream>
#include<cstdio>
#include<deque>
using namespace std;

const double eps = 1e-6;
const int Inf = 1<<30;

class Point{
public:
	double x, y;
	int left, right;//供light使用
	int cost;
	Point(double x = 0, double y = 0, int cost = 0){
		this->x = x;
		this->y = y;
		this->cost = cost;
	}
	Point Sub(Point a){
		return Point(x - a.x, y - a.y);
	}
	double mulCross(Point a){
		return (x*a.y-y*a.x);
	}
};

Point center;//多边形的中心点

class Solve{
public:
	int N, M;//多边形的顶点个数 光源的个数
	Point vert[35];
	Point light[1010];
	int ans;
	int judge(double t1,double t2){
		if (t1*t2 < -eps) return -1;
		return t1*t2 > eps;
	}

	void getEdge(int ind){
		bool flag[35];
		memset(flag, false, sizeof(flag));
		for (int i = 0; i < N; i++){
			Point ab = vert[i + 1].Sub(vert[i]);
			Point ao = center.Sub(vert[i]);
			Point ai = light[ind].Sub(vert[i]);
			double t1 = ab.mulCross(ao);
			double t2 = ab.mulCross(ai);
			if (judge(t1, t2) < 0) flag[i] = true;
		}
		if (flag[0] && flag[N - 1]){
			int left = N - 1, right = N;
			while (flag[left]){
				light[ind].left = left;
				left--;
			}
			while (flag[right-N]){
				light[ind].right = right;
				right++;
			}
		}
		else{
			int left = 0, right = N - 1;
			while (!flag[left]) left++;
			light[ind].left = left;
			while (!flag[right]) right--;
			light[ind].right = right;
		}
		light[ind].right++;//左闭右开
		if (light[ind].right < light[ind].left)
			light[ind].right += N;
	}

	void Deal(){
		for (int i = 0; i < M; i++)
			getEdge(i);	
		ans = Inf;
		int dp[70];
		for (int i = 0; i < N; i++){
			for (int m = 0; m < 70; m++) dp[m] = Inf;
			dp[i] = 0;
			for (int j = 0; j < N; j++){
				int t = i + j;
				for (int k = 0; k < M; k++){
					if (light[k].left > t) continue;
					int ind = min(light[k].right, i + N);
					dp[ind] = min(dp[ind], light[k].cost + dp[t]);
				}
			}
			ans = min(ans, dp[i + N]);
		}
		if (ans == Inf) cout << "Impossible.\n";
		else cout << ans << endl;
	}
};

int main(){
	Solve a;
	while (cin >> a.N){
		if (a.N == 0) break;
		center.x = center.y = 0;
		for (int i = 0; i < a.N; i++){
			cin >> a.vert[i].x >> a.vert[i].y;
			center.x += a.vert[i].x;
			center.y += a.vert[i].y;
		}
		a.vert[a.N] = a.vert[0];
		center.x = center.x / a.N;
		center.y = center.y / a.N;
		cin >> a.M;
		for (int i = 0; i < a.M; i++)
			cin >> a.light[i].x >> a.light[i].y >> a.light[i].cost;
		a.Deal();
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值