- 状态的定义与转移
- 贪心思想对动态规划的优化
确定状态
- 青蛙的烦恼 LSGDOJ 1852
题目描述
池塘中有n片荷叶恰好围成了一个凸多边形,有一只小青蛙恰好站在1号荷叶上,小青蛙想通过最短的路程遍历所有的荷叶(经过一个荷叶一次且仅一次),小青蛙可以从一片荷叶上跳到另外任意一片荷叶上。
输入
第一行为整数n,荷叶的数量。
接下来n行,每行两个实数,为n个多边形的顶点坐标,按照顺时针方向给出。保证不会爆double。
输出
遍历所有荷叶最短路程,请保留3位小数。
样例输入
4 50.0 1.0 5.0 1.0 0.0 0.0 45.0 0.0
样例输出
50.211
提示
对于所有数据,0<n<=720
分析:
状态定义:
-
从 i 点出发,顺时针or 逆时针 走 j 步的最少距离(最优解),但是状态转移很麻烦,首先第一维是 j,第二维是起点,但是状态转移很难表示,从 i 点出发,还走 j-1 步?不一定,可以从从其他点,而不是相邻的点转移而来。所以走了,或者要走的节点,没有表示到dp状态中,转移方程很难写出来,加一维枚举从哪里来,时间不允许。步数也难确定了,于是考虑别的状态定义。
-
考虑顺序, 从 I 结点,访问 剩下的 i+1,然后走i+2,...,i+j-1的最优解。从 i+j-1 结点开始,访问 剩下的i, i + 1,i+2,...,i+j-2 的最优解。
有了访问的顺序定义,状态转移就得到是:
贪心思想体现于凸多边形走法,不存在交叉路线。

#include <bits/stdc++.h> using namespace std; const int maxn = 1000; struct Node { double x,y; }nodes[maxn]; double dist[maxn][maxn]; double dp[maxn][maxn][2]; int main(int argc, char const *argv[]) { freopen("in.txt","r",stdin); int n; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%lf%lf",&nodes[i].x,&nodes[i].y); } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dist[i][j] = sqrt((nodes[i].x-nodes[j].x)*(nodes[i].x-nodes[j].x)+(nodes[i].y-nodes[j].y)*(nodes[i].y-nodes[j].y)); for (int i = 1; i <= n; ++i) dp[i][1][0] = dp[i][1][1] = 0; for(int j=2;j<=n;j++) { for(int i=1;i<=n;i++) { dp[i][j][0] = min(dist[i][i+1]+dp[i+1][j-1][0],dist[i][i+j-1]+dp[i+1][j-1][1]); dp[i][j][1] = min(dist[i][i+j-1]+dp[i][j-1][0],dist[i+j-1][i+j-2]+dp[i][j-1][1]); } } printf("%lf ",min(dp[1][n][0],dp[1][n][1])); return 0; }