zoukankan      html  css  js  c++  java
  • hdu 5135 Little Zu Chongzhi's Triangles 状压DP

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5135

    从大到小贪心是不对的!

    比如

    4

    30 40 50 89

    这组数据 结果应该是600 如果取后三条边只会是一个细长细长的三角形根本没多大面积

    只不过因为数据水然后贪心碰巧能过而已

    看到网上题解很多人用贪心我好郁闷...

    状压DP

    看到边的数目很少就应该想到这个了

    先枚举可能出现的所有三角形 记录他们使用了那些边还有他们的面积

    然后再枚举状态之中 套一层遍历所有三角形的循环 如果当前状态和三角形的选边不冲突 则更新选中这三条新边之后的状态

    转移方程:

    dp[i | tri[j].choice] = max(dp[i | tri[j].choice], dp[i] + tri[j].S);

    然后枚举的时候用了一些技巧

    比如:

    if((i & (tri[j].choice)) == 0)  //若第j个三角形所需的三条边都未使用

    #include <cstdio>
    #include <cstdlib>
    #include <ctime>
    #include <iostream>
    #include <cmath>
    #include <cstring>
    #include <algorithm>
    #include <stack>
    #include <set>
    #include <queue>
    #include <vector>
    
    using namespace std;
    
    typedef long long ll;
    
    const int maxn = (1 << 14);
    const int maxm = 15;
    
    typedef struct
    {
        int choice; //二进制记录哪些三角形已经用过 1表示用了 0表示没用
        double S;   //面积
    }Node;    
          
    Node tri[maxn]; //暴力枚举每一个可能的三角形
    double dp[maxn];
    int a[maxm];
    
    double area(int a, int b, int c)    //海伦公式算面积
    {
        double p = (double)(a + b + c) / 2;
        return sqrt(p*(p-a)*(p-b)*(p-c));
    }
    
    int main()
    {
        //freopen("in.txt", "r", stdin);
    
        int n;
        while(scanf("%d", &n) == 1 && n != 0)
        {
            memset(dp, 0, sizeof(dp));
    
            for(int i = 0; i < n; i++)
                scanf("%d", &a[i]);
    
            sort(a, a + n); //先升序排
    
            int cnt = 0;    //记录一共有几个可能的三角形
            for(int i = 0; i < (1 << n); i++)//三条边三条边的枚举三角形 复杂度仅约10^4 绰绰有余
            {
                int tmp[maxm];  //暂存已选中的边 
                int loc = 0;    //记录选了几条边
                for(int j = 0; j < n; j++)
                {
                    if(i & (1 << j))
                        tmp[loc++] = a[j];  //因为a[]已经排过序 所以tmp[]一定也是升序的 所以仅需满足“tmp[0] + tmp[1] <= tmp[2]”即可为三角形
                }
    
                if(loc != 3 || tmp[0] + tmp[1] <= tmp[2])   //“三条边”并且“可构成三角形”才能继续往下走
                    continue;
    
                tri[cnt].choice = i;
                tri[cnt].S = area(tmp[0], tmp[1], tmp[2]);
                cnt++;
            }
    
            double ans = 0;
            for(int i = 0; i < (1 << n); i++)   //枚举每一个状态
            {
                for(int j = 0; j < cnt; j++)    //枚举每一个三角形
                {
                    if((i & (tri[j].choice)) == 0)  //若第j个三角形所需的三条边都未使用
                    {
                        dp[i | tri[j].choice] = max(dp[i | tri[j].choice], dp[i] + tri[j].S);//更新加入第j个三角形的三条边后的状态
                        if(dp[i | tri[j].choice] > ans) //计算过程不断更新最大值
                            ans = dp[i | tri[j].choice];
                    }
                }
            }
    
            printf("%.2f
    ", ans);
    
        }
    
        return 0;
    }
  • 相关阅读:
    mysql 常用命令行
    mysql常用命令
    Mac os安装wget
    linux下给文件夹或者目录赋权
    Python学习相关资料
    Mac常用的一些操作
    Mac os安装git及 git及githup的使用
    Linux磁盘占用100%解决方法
    page-break-after:always
    工具
  • 原文地址:https://www.cnblogs.com/dishu/p/4294967.html
Copyright © 2011-2022 走看看