您的位置:首页 > 其它

Hdu 5135 -Little ZuChongzhi's Triangles

2016-10-31 17:25 288 查看
Hdu 5135 -Little ZuChongzhi's Triangles
题意:给n跟棍子,选出棍子来组成一些三角形,求组成的三角形的总面积的最大值.
分析:一般求最优解可以考虑这几种解法:dp,贪心,搜索,枚举。
做这题的时候卡了两三个小时,一直找不到错误,居然是因为海伦公式中三条边相加除以2然后直接取整导致错误了,正好样例的三条边能整除2.....细节问题还是很重要,必须要有检查类型,数组是否越界的意识才行。经常在运算出现类型错误,类型溢出等问题上栽跟头。
(1)、很明显的无后效性,状态中要表示棍子的选取情况,可以考虑状压dp。定义dp[i][s]为i根棍子中选取s中的棍子能组成的最大面积。状态转移如下:
 #include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#define INF 0x3f3f3f3f
#define LL long long
#define N 100
using namespace std;

double dp[15][4100], a[15];
double area(double i, double j, double k)
{
if (i + j <= k) return 0.0;
double p = (i + j + k) / 2;
return sqrt(p * (p - i) * (p - j) * (p - k));
}
int main()
{
int n;
while (scanf("%d", &n), n){
for (int i = 0; i < n; i++) scanf("%lf", a+i);
for (int i = 3; i <= n; i++){
for (int s = (1<<3) - 1; s < 1<<i; s++){
dp[i][s] = 0;
int y = i-1;
if (s >> y & 1){
int t[15], k = 0;
int tm = s & ~(1 << y);
for (int x = 0; x < y; x++)
if (tm >> x & 1) t[k++] = x;
for (int j = 0; j < k-1; j++){
for (int x = j+1; x < k; x++){
int b = t[j], c = t[x];
dp[i][s] = max(dp[i][s], area(a[b], a[c], a[y]) + dp[i-1][tm & ~(1<<b) & ~(1<<c)]);
}
}
}
else dp[i][s] = dp[y][s];
}
}
printf("%.2f\n", dp
[(1<<n)-1]);
}
return 0;
}

(2)、棍子最多12根,直接搜索,不加任何剪枝都可以过。搜索步骤很清晰,每一次选取3根棍子来组成三角形,然后继续深搜,返回之后尝试不选之前的棍子的组合,然后取最大值即可。不过可以排个序,显而易见地可以剪掉一些部分。
 #include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <functional>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;

int vis[50];
double ans;
double a[20];
int n;
double area(double i, double j, double k)
{
double p = (i + j + k) / 2;
return sqrt(p * (p - i) * (p - j) * (p - k));
}
void dfs(double s)
{
ans = max(ans, s);
for (int i = 0; i < n - 2; i++){
if (vis[i]) continue;
vis[i] = 1;
for (int j = i + 1; j < n-1; j++){
if (a[j] + a[j+1] <= a[i]) break;//不能组成三角形直接剪枝
if (vis[j]) continue;
vis[j] = 1;
for (int k = j + 1; k < n; k++){
if (a[j] + a[k] <= a[i]) break;//不能组成三角形直接剪枝
if (vis[k]) continue;
vis[k] = 1;
dfs(s + area(a[i], a[j], a[k]));
vis[k] = 0;
}
vis[j] = 0;
}
vis[i] = 0;
}
}
int main()
{
while (scanf("%d", &n), n){
for (int i = 0; i < n; i++) scanf("%lf", a + i);
sort(a, a + n, greater<int>());
memset(vis, 0, sizeof(vis));
ans = 0.0; dfs(0);
printf("%.2f\n", ans);
}
return 0;
}
(3)、刚开始看到这题,直觉就是选取长的来组成三角形,这样组成的三角形的面积一定是最大的,贪心从大开始选取,每次选取相邻的三根棍子,如果能组成三角形就这样组合,不能组合的话长的棍子就没用了。但是这样的贪心策略是错误的,因为不能保证把长的组合在一起对短的棍子不会有影响,短的棍子与长的棍子组合起来面积可以更大。

比如说这样一种情况:

假设这两条边很长很长,那么那个很小的三角形是不会比那个等边三角形的面积大的。虽说这题中的边长小于100,可能不会出现这种情况,但是这样的策略也是错误的,刚刚生成了很多小于100的随机数跑,出现了一些错误的例子。
比如说这个:
11
57 10 38 37 20 18 54 53 44 1542
这题数据很水,贪心也能过,dp的复杂度比贪心大很多,却也是0ms,可以证明数据有多水了。
其实以一般平常的题目来讲,正解为贪心的题目数据量应该会很大的,因为贪心确定好策略之后一般都是O(n)的。(当然很多贪心策略都要事先排序,所以需要nlog(n)处理一遍)。
但是这题才12根棍子,用贪心的话个人感觉好像有点不合常理…..
 #include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;

double a[50];
double area(double i, double j, double k)
{
double p = (i + j + k) / 2;
return sqrt(p * (p - i) * (p - j) * (p - k));
}
int main()
{
int n;
while (scanf("%d", &n), n){
for (int i = 0; i < n; i++) scanf("%lf", a + i);
sort(a, a + n);
int flag = 1;
double ans = 0;
for (int i = n-1; i >= 2; i--)
if (a[i-1] + a[i-2] > a[i]){
ans += area(a[i], a[i-1], a[i-2]);
flag = 0, i -= 2;
}
flag ? puts("0.00") : printf("%.2f\n", ans);
}
return 0;
}
 
 
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: