Hdu 5135 -Little ZuChongzhi's Triangles

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[n][(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;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值