ZOJ 1679 Telescope(区间DP变型题)

题意:

给定一个圆和圆周上面的 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;
}

转载于:https://www.cnblogs.com/kedebug/archive/2013/04/08/3007300.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值