题意:
给定一个圆和圆周上面的 N 个点,选择其中的 M 个,按照在圆周上的顺序连成一个 M 边形,使得它的面积最大。
(黑书 147 圆和多边形)
思路:
1. 我一开始尝试的是 dp[i, j] 表示前 i 边形在前 j 个点上的最大面积。想来想去,因为三角形的起点和终点无法固定,则要加强下命题;
2. 对于此类循环类型的动态规划,一般想办法和区间 DP 挂上钩。dp[i, d, j] 表示以 i 点出发,且以 [i, i+d-1] 为区间,找到 j 个点的最大面积;
3. 这样的话,枚举 i,输出 dp[i, N, M] 即可。但是仍然无法保证三角形的第三个点的位置。还需要再次加强命题;
4. dp[i, d, j] 表示以 i 点出发,且以 [i, i+d-1] 为区间,以 i+d-1 点结束。找到 j 个点使面积最大的情况。问题变得明朗了;
5. dp[i, d, j] = max(dp[i, d, j], dp[i, k, j-1] + s[i, i+k-1, i+d-1]); 最终时间复杂度被加强为 O(N * N * M * M);
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
using namespace std;
const int MAXN = 41;
const double PI = acos(-1.0);
double p[MAXN], s[MAXN][MAXN][MAXN], dp[MAXN][MAXN][MAXN];
class CVector {
public:
CVector(double _x, double _y) : x(_x), y(_y) {}
double operator * (const CVector& other) const {
return x * other.y - y * other.x;
}
private:
double x, y;
};
void initdata(int n) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k < n; k++) {
CVector v1(cos(p[i]*PI*2), sin(p[i]*PI*2));
CVector v2(cos(p[j]*PI*2), sin(p[j]*PI*2));
CVector v3(cos(p[k]*PI*2), sin(p[k]*PI*2));
s[i][j][k] = fabs((v1*v2 + v2*v3 + v3*v1) / 2);
}
}
}
}
int main() {
int N, M;
while (scanf("%d%d", &N, &M) && N && M) {
for (int i = 0; i < N; i++)
scanf("%lf", &p[i]);
initdata(N);
for (int d = 0; d <= N; d++)
for (int i = 0; i < N; i++)
for (int m = 0; m <= M; m++)
dp[i][d][m] = 0.0;
for (int d = 3; d <= N; d++) {
for (int i = 0; i < N; i++) {
for (int m = 3; m <= M && m <= d; m++)
for (int k = m-1; k <= d-1; k++)
dp[i][d][m] = max(dp[i][d][m], dp[i][k][m-1] + s[i][(i+k-1)%N][(i+d-1)%N]);
}
}
double ans = 0.0;
for (int i = 0; i < N; i++)
for (int d = M; d <= N; d++)
ans = max(ans, dp[i][d][M]);
printf("%.6lf\n", ans);
}
return 0;
}