[计算几何] pku2079 凸包+旋转卡壳
2 评论80次阅读2007.09.13 13:40 作者:Felicia 编辑
先求凸包,然后再用旋转卡壳方法求解。
具体做法是枚举三角形的第一个点i,设j = i + 1,k = j + 1。然后做以下操作:
- 计算i,j,k构成的三角形面积a1和i,j,k + 1构成的三角形面积a2,如果a2 < a1,则进行下一步,否则k++,重复此步。
- 记录此时的三角形面积b,如果b < preb(就是上一个j对应的三角形面积)j++,转第一步,否则退出。
可以证明这个算法的复杂度为O(n2)。具体实现见代码。
[动态规划] pku1038 状态压缩
发表评论24次阅读2007.09.12 20:44 作者:Felicia 编辑
经典的状态压缩DP,《算法艺术与信息学竞赛》的例题。f[i][j]表示前i行,最后两行状态为二进制数j,嵌入的最多芯片数。第i行到第i+1行用DFS进行状态转移。
由于第i+1行只和第i行有关,故可以用滚动数组优化。
[计算几何] pku1092 奇特的技巧
发表评论36次阅读2007.09.07 19:37 作者:Felicia 编辑
题目要求统计一个平面图中所有边数为k的面的个数。应该是个经典问题。说说我的算法吧。
枚举每条边,做以下的基本步骤。
基本步骤:以这条边作起始边,不断地找下一条”最左转”的边,并且标记每个点的访问次数,直到某个点第3次被访问为止。
经过这个步骤之后,得到一个顶点序列。容易知道,当且仅当这个顶点序列是2-重复(就是形如12341234这样),并且是逆时针旋转的,那么就是一个面。
接下去我们就把所有找到的边数为k面进行hash去重,就得到答案啦。
貌似我想的这个算法不够好,如果有更好的算法,欢迎和我讨论。
[计算几何] pku1279 多边形的核
发表评论39次阅读2007.08.25 15:56 作者:Felicia 编辑
求多边形的核。用半平面交算法。
下面是我的代码
Author: WHU_GCC
Created Time: 2007-8-25 15:43:03
File Name: pku1279.cpp
Description:
**********************************************************************/
#include <iostream>
#include <cmath>
using namespace std;
#define out(x) (cout << #x << ": " << x << endl)
const int maxint = 0x7FFFFFFF;
typedef long long int64;
const int64 maxint64 = 0x7FFFFFFFFFFFFFFFLL;
template <class T> void show(T a, int n) {for (int i = 0; i < n; ++i) cout << a[i] << ' '; cout << endl; }
template <class T> void show(T a, int r, int l) {for (int i = 0; i < r; ++i) show(a[i], l); cout << endl; }
#define EPS 1e-10
#define MaxN 3001
struct point
{
double x, y;
};
struct cp
{
int n;
point p[MaxN];
};
point intersectL(double a1, double b1, double c1, double a2, double b2, double c2)
{
point ret;
ret.y = (a1 * c2 - c1 * a2) / (b1 * a2 - a1 * b2);
if (fabs(a2) < EPS)
ret.x = -(b1 * ret.y + c1) / a1;
else
ret.x = -(b2 * ret.y + c2) / a2;
return ret;
}
bool isEqual(point inpA, point inpB)
{
return (fabs(inpA.x - inpB.x) < EPS && fabs(inpA.y - inpB.y) < EPS);
}
double Cross(point inpA, point inpB, point inpC)
{
return (inpB.x - inpA.x) * (inpC.y - inpA.y) - (inpC.x - inpA.x) * (inpB.y - inpA.y);
}
void Get_line(point inpA, point inpB, double &a1, double &b1, double &c1)
{
a1 = inpB.y - inpA.y;
b1 = inpA.x - inpB.x;
c1 = inpA.y * (inpB.x - inpA.x) - inpA.x * (inpB.y - inpA.y);
}
cp cut(point inpA, point inpB, cp incp)
{
cp ret;
point cross;
int i, j;
double t1, t2;
double a1, b1, c1, a2, b2, c2;
ret.n = 0;
for (i = 0; i < incp.n; i++)
{
j = i + 1;
t1 = Cross(inpA, inpB, incp.p[i]);
t2 = Cross(inpA, inpB, incp.p[j]);
if (t1 < EPS && t2 < EPS)
{
ret.p[ret.n++] = incp.p[i];
ret.p[ret.n++] = incp.p[j];
}
else if (t1 > EPS && t2 > EPS)
continue;
else
{
Get_line(inpA, inpB, a1, b1, c1);
Get_line(incp.p[i], incp.p[j], a2, b2, c2);
cross = intersectL(a1, b1, c1, a2, b2, c2);
if (t1 < EPS)
{
ret.p[ret.n++] = incp.p[i];
ret.p[ret.n++] = cross;
}
else
{
ret.p[ret.n++] = cross;
ret.p[ret.n++] = incp.p[j];
}
}
}
if (ret.n == 0) return ret;
for (i = 1, j = 1; i < ret.n; i++)
if (!isEqual(ret.p[i - 1], ret.p[i]))
ret.p[j++] = ret.p[i];
ret.n = j;
if (ret.n != 1 && isEqual(ret.p[ret.n - 1], ret.p[0])) ret.n--;
ret.p[ret.n] = ret.p[0];
return ret;
}
int main()
{
int ca;
int n;
cp input, ret;
for (scanf("%d", &ca); ca--;)
{
scanf("%d", &n);
input.n = n;
for (int i = 0; i < n; i++)
scanf("%lf%lf", &input.p[i].x, &input.p[i].y);
input.p[input.n] = input.p[0];
ret = input;
for (int i = 0; i < input.n; i++)
ret = cut(input.p[i], input.p[i + 1], ret);
double area = 0.0;
for (int i = 0; i < ret.n; i++)
area += ret.p[i].x * ret.p[(i + 1) % n].y - ret.p[(i + 1) % n].x * ret.p[i].y;
printf("%.2lf\n", abs(area / 2.0));
}
return 0;
}
[计算几何] pku1418 圆的离散化
发表评论34次阅读2007.08.24 22:43 作者:Felicia 编辑
强烈推荐此题!这个题目我做了很久,始终不得其解。后来我向dm求教,他发来代码。我对照数据才过的。
先考察一下这个问题的性质。
性质1:任何一个圆都覆盖了一个闭区域。
性质2:对于任意一个点,覆盖它的最上面的那个圆,一定是可见的。
性质3:如果一个圆不可见(它被完全覆盖),那么它的边界是被完全覆盖的。
性质4:n 个圆最多有2(n-1)2个交点,这些交点把 n 个圆分成最多2(n-1)2条小圆弧。
性质5:对于每个小圆弧,要么它全被覆盖,要么它全不被覆盖。
根据性质1和性质2,问题转化为恰当地找出一些点,对于每个点,把覆盖它的最上面的圆标记为可见。
根据性质3,这些点一定在所有圆的边界集合内。
根据性质5,所有小圆弧构成边界集合。每个小圆弧上只要任意取一个点就能代表整个小圆弧(边界)。不妨取中点。
至此得到算法:取所有小圆弧的中点,对每个点找到覆盖它的最上面的圆。
根据性质4,最多取2(n-1)2个点。对每个点找到覆盖它的最上面的圆,需要O(n)次运算。总复杂度是O(n3)。
其实此算法还有冗余,有些内部小圆弧可以不用考虑,但是我没想出怎么优化。有谁知道更好的算法,请联系我。blog留言或者qq交谈或者发邮件都可以。
下面是我的代码
Author: WHU_GCC
Created Time: 2007-8-24 12:33:03
File Name: pku1418.cpp
Description:
**********************************************************************/
#include <iostream>
#include <algorithm>
#include <vector>
#include <complex>
#include <cmath>
using namespace std;
#define out(x) (cout << #x << ": " << x << endl)
const int maxint = 0x7FFFFFFF;
typedef long long int64;
const int64 maxint64 = 0x7FFFFFFFFFFFFFFFLL;
template <class T> void show(T a, int n) {for (int i = 0; i < n; ++i) cout << a[i] << ' '; cout << endl; }
template <class T> void show(T a, int r, int l) {for (int i = 0; i < r; ++i) show(a[i], l); cout << endl; }
typedef complex <double> xy;
const double PI = acos(-1.0);
double normalize(double r)
{
while (r < 0.0) r += 2 * PI;
while (r >= 2 * PI) r -= 2 * PI;
return r;
}
int highest_cover(xy p, vector <xy> &points, vector <double> &rs)
{
for (int i = rs.size() - 1; i >= 0; i--)
if (abs(points[i] - p) < rs[i])
return i;
return -1;
}
int main()
{
while (1)
{
int n;
cin >> n;
if (!n) break;
vector <xy> points;
vector <double> rs;
for (int i = 0; i < n; i++)
{
double x, y, r;
cin >> x >> y >> r;
points.push_back(xy(x, y));
rs.push_back(r);
}
vector <bool> visible(n, false);
for (int i = 0; i < n; i++)
{
vector <double> rads;
rads.push_back(0.0);
rads.push_back(2.0 * PI);
for (int j = 0; j < n; j++)
{
double a = rs[i];
double b = abs(points[j] - points[i]);
double c = rs[j];
if (a + b < c || a + c < b || b + c < a) continue;
double d = arg(points[j] - points[i]);
double e = acos((a * a + b * b - c * c) / (2 * a * b));
rads.push_back(normalize(d + e));
rads.push_back(normalize(d - e));
}
sort(rads.begin(), rads.end());
for (int j = 0; j < rads.size() - 1; j++)
{
double rad = (rads[j + 1] + rads[j]) / 2.0;
double diff = 4E-13;
for (int k = -1; k <= 1; k += 2)
{
int t = highest_cover(xy(points[i].real() + (rs[i] + diff * k) * cos(rad),
points[i].imag() + (rs[i] + diff * k) * sin(rad)),
points, rs);
if (t != -1) visible[t] = true;
}
}
}
int ans = 0;
for (int i = 0; i < n; i++)
if (visible[i])
ans++;
cout << ans << endl;
}
return 0;
}
