您的位置:首页 > 其它

ZOJ - 3735 Cake(凸包,区间dp,最优三角剖分)

2017-07-06 18:20 369 查看
对着kuangbin博客开始刷区间dp!

按照题目的要求先用Graham扫描法求出凸包,如果凸包上点的数目正好等于输入点的数目,也就说明输入的点恰好构成凸包,否则输出“I can't cut.”。

求出凸包后,注意到这是一个环,所以只要把环剪开,就可以用区间dp做了。

定义状态dp[i][j]表示i到j这段区间内最优三角剖分的代价,注意此时i和j之间已经有边了。

所以状态转移方程为:dp[i][j] = min(dp[i][k] + dp[k][j] + cost[i][k] + cost[j][k]) (i < k < j)

至于为什么要算两个代价呢,不是一次只切一刀吗?

其实这是由我们定义的状态决定的,在定义的状态中,dp[i][j]默认i和j已经有边了,如果我们只切i-k这一刀,而不切k-j这一刀,那么子状态dp[k][j]就不符合定义,因为k和j之间没有边。

代码如下:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>

using namespace std;

const int INF = 0x3f3f3f3f;
struct Point
{
int x, y;
};
Point p[500];
int dp[500][500];
Point stack[500]; // Graham扫描法要用到的栈
int top; // 用来标记栈顶
int n, m;

// 计算两点间的欧式距离
double dist(Point p1, Point p2)
{
return sqrt((double)(p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}

// 计算向量(p1 - p0)叉乘向量(p2 - p0)的结果,符号为正,则方向向上。
int cross(Point p0, Point p1, Point p2)
{
return (p1.x - p0.x) * (p2.y - p0.y) - (p1.y - p0.y) * (p2.x - p0.x);
}

bool cmp(Point p1, Point p2)
{
int tmp = cross(p[0], p1, p2);
if (tmp)
return tmp > 0;
return dist(p[0], p1) < dist(p[0], p2);
}

void Graham(int n)
{
for (int i = 1; i < n; i++)
if (p[i].y < p[0].y || (p[i].y == p[0].y && p[i].x < p[0].x))
swap(p[i], p[0]);
sort(p + 1, p + n, cmp); // 以p0为原点的极角排序
stack[0] = p[0];
stack[1] = p[1];
top = 2;
for (int i = 2; i < n; i++)
{
while (top >= 2 && cross(stack[top - 2], stack[top - 1], p[i]) < 0)
top--;
stack[top++] = p[i];
}
}

// 计算题目中定义的代价
int cost(int a, int b)
{
if (abs(a - b) == 1)
return 0;
return abs(p[a].x + p[b].x) * abs(p[a].y + p[b].y) % m;
}

int main()
{
//freopen("test.txt", "r", stdin);
while (~scanf("%d%d", &n, &m))
{
for (int i = 0; i < n; i++)
scanf("%d%d", &p[i].x, &p[i].y);
Graham(n);
if (top != n || n < 3)
{
printf("I can't cut.\n");
continue;
}
memset(dp, 0, sizeof(dp));
for (int len = 2; len < n; len++)
for (int i = 0; i < n; i++)
{
int j = i + len;
if (j >= n)
break;
dp[i][j] = INF;
for (int k = i + 1; k < j; k++)
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j] + cost(i, k) + cost(k, j));
}
printf("%d\n", dp[0][n - 1]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息