题目链接:10641 - Barisal Stadium
题意:逆时针给定n个点,在给m个灯,每一个灯有一个花费,要求最小花费使得全部边能被灯照到
思路:用向量叉积推断向量的顺逆时针关系,从而预处理出每一个灯能照到的边,然后因为n个点是环的,所以能够直接扩大两倍,dp时候去枚举起点就可以
状态为dp[i]表示如今照到i条边之前的边所有照亮须要的最小花费
代码:
#include <stdio.h> #include <string.h> const double eps = 1e-6; const int N = 105; const int M = 1005; int n, m, i, j, dp[N]; bool flag[N]; #define INF 0x3f3f3f3f #define min(a,b) ((a)<(b)?(a):(b)) struct Point { double x, y; Point(double x = 0, double y = 0) { this->x = x; this->y = y; } void read() { scanf("%lf%lf", &x, &y); } } p[N], o; struct Q { int l, r, c; } q[M]; bool judge(Point p0, Point p1, Point p2) { return ((p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y)) < -eps; } Q tra(Point t, int c) { Q ans; ans.c = c; memset(flag, false, sizeof(flag)); for (int i = 0; i < n; i++) { if (judge(t, p[i], p[i + 1])) flag[i] = true; } if (flag[0] && flag[n - 1]) { int l = n - 1, r = n; while (flag[l - 1]) l--; while (flag[r - n + 1]) r++; ans.l = l; ans.r = r; } else { int l = 0, r = n - 1; while (!flag[l]) l++; while (!flag[r]) r--; ans.l = l; ans.r = r; } if (ans.r < ans.l) ans.r += n; return ans; } bool solve() { int ans = INF; for (int i = 0; i < n; i++) { memset(dp, INF, sizeof(dp)); dp[i] = 0; for (int j = 0; j < n; j++) { int r = i + j; for (int k = 0; k < m; k++) { if (q[k].l > r) continue; int now = min(i + n, q[k].r + 1); dp[now] = min(dp[now], dp[r] + q[k].c); } } ans = min(ans, dp[i + n]); } if (ans == INF) return false; printf("%d ", ans); return true; } int main() { while (~scanf("%d", &n) && n) { for (i = 0; i < n; i++) p[i].read(); p[n] = p[0]; scanf("%d", &m); Point tmp; int c; for (i = 0; i < m; i++) { tmp.read(); scanf("%d", &c); q[i] = tra(tmp, c); } if (!solve()) printf("Impossible. "); } return 0; }