从大二起,也做了不少的动态规划题,但对于很多动态规划状态还是一知半解,究其原因,是在平时做题的过程中,不求甚解,遇到问题只是匆匆看看题解,而不深入探讨。导致很多问题都模模糊糊的过去了,做题也等于没有做。从现在开始正式的反思总结。新的一年,新的开始^_^
题意:这道题是给出一个圆,上面有n个点,从中选出m个点,选择一种方案使得构成的m多边形面积最大(m个点依次顺序连接形成多边形)。
题解:(动态规划很多时候都需要枚举子问题的状态,从而推出所需问题的最优策略,这样,对于子问题的枚举策略就显得至关重要。既然是枚举,所有的情况都不能遗漏,枚举条件的选取恰当与否决定了问题能否顺利解决)这题的数据规模是40,因为涉及到最大面积,考虑动态规划,根据题意,猜测是区间DP,对于DP参数的设定:
dp(i, j, k) 表示以i开始,以j结束的拥有k个点的多边形的最大面积,这昂最总答案是max{dp(i, j, m)}.
状态转移方程:dp(i, j, k) = max{ dp(i, j, k) , dp(i, q, k - 1) + Triangle(i, q, j) }.
代码:
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const double PI = acos(-1.0); const int INF = 0x3f3f3f3f; #define N 50 int n, m; double dp[N][N][N], _area[N][N][N]; struct Point{ double x, y; }P[N]; double cross(Point a, Point b, Point c) { return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y); } double area(Point a, Point b, Point c) { return fabs(cross(a, b, c)/2.0); } void init(){ for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) for(int k = 0; k < n; k++){ _area[i][j][k] = area(P[i], P[j], P[k]); } memset(dp, 0, sizeof dp); } void DP(){ for(int i = 0; i < n; i++) for(int j = i + 2; j < n; j++) for(int k = 3; k <= m; k++) for(int q = i + k - 2 ; q < j; q++) dp[i][j][k] = max(dp[i][j][k], dp[i][q][k-1] + _area[i][j][q]); double ans = -INF; for(int i = 0; i < n; i++) for(int j = i + m - 1; j < n; j++){ ans = max(ans, dp[i][j][m]); } printf("%.6lf\n", ans); } int main(){ // freopen("2016.txt", "r", stdin); while(scanf("%d%d", &n, &m) && n + m){ for(int i = 0; i < n; i++){ double tmp; scanf("%lf", &tmp); tmp = 2 * PI * tmp; P[i].x = cos(tmp); P[i].y = sin(tmp); } init(); DP(); } return 0; }