UVA1354枚举二叉树

枚举二叉树的方式有很多,其中一种方式就是每次从一个集合中挑选两个点出来,一个做左子树,一个做右子树,直到集合中只剩下一个节点位置

那么问题就在于如何挑选?因为必须创建节点加入集合当中去。

我一开始的思路是使用vecoter,每挑选一个就删除一个,并把新创建的节点加入到vector中去。

但是这种方式是行不通的,因为当回溯的时候,vector不可能恢复原样。所以使用的方法只能是在数组的末尾添加,每挑选一个节点,就设置一次vis,当第cur次挑选的时候,挑选的范围是num_node + cur.这很好理解,第cur次挑选的时候,需要考虑前面cur-1次挑选完后新创建的节点。

本质还是回溯法一共有num_node - 1个步骤,每个步骤的可以选择的节点数是有限制的

需要注意的是,挑出来的两个节点放在左面和右面的结果是不一样的。所以需要考虑到所有的情况(挑选的时候的第二层循环中的j,是从0开始的,而不是从i开始的)

如果左右是一样的,那么可以从i开始

下面附上代码:

//我发现网上写的是些什么玩意
//枚举二叉树
//每次枚举两个节点,并将两个节点合并成一个节点,然后集合中,供下次选取
//使用vector,和vector中的erase函数
//至于汝佳写的枚举子集的方式,先放一放吧,看的不是很懂
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn = 6;

double max_room,room;

int num_node;
struct node
{
    double w;
    double left;
    double right;
    node(double c = 0,double a = 0,double b = 0):w(c),left(a),right(b){}
}Node[1<<maxn];//1<<maxn足够,因为这是一棵有maxn层的完全树,而我们要求的树叶只有maxn层的树。
int vis[1<<maxn];

void DFS(int cur)
{
    if(cur == num_node -1 )
    {
        double now_room = Node[2 * (num_node - 1)].left + Node[2 * (num_node - 1)].right;
        if(now_room > max_room && now_room < room)
        {
            max_room = now_room;
        }
    }
    else
    {
        for(int i = 0;i < num_node + cur;i++)//i挑的是左树,j挑的是右树
        {
            for(int j = 0;j < num_node + cur;j++)
            {
                if(i == j)
                    continue;
                if(!vis[i] && !vis[j])
                {
                    //都没有访问过
                    vis[i] = vis[j] = 1;
                    Node[num_node + cur].w = Node[i].w + Node[j].w;
                    double left = Node[j].w / Node[num_node + cur].w;
                    double right = Node[i].w /Node[num_node + cur].w;
                    Node[num_node + cur].left = max(left + Node[i].left,Node[j].left - right);
                    Node[num_node + cur].right = max(right + Node[j].right,Node[i].right - left);
                    DFS(cur + 1);
                    vis[i] = vis[j] = 0;
                }
            }
        }
    }

}



int main()
{
#ifdef local
    freopen("input.txt","r",stdin);
#endif
    int kase;
    scanf("%d",&kase);
    for(int i = 0;i < kase;i++)
    {
        max_room = 0;
        memset(vis,0,sizeof(vis));
        memset(Node,0,sizeof(Node));
        scanf("%lf%d",&room,&num_node);
        for(int j = 0;j < num_node;j++)
        {
            scanf("%lf",&Node[j].w);
        }
        DFS(0);
        if(max_room == 0)
            printf("-1\n");
        else
            printf("%.12lf\n",max_room);

    }
    return 0;
}

至于汝佳大神的动态规划的实现,我暂时还看不太懂,以后再看吧

猜你喜欢

转载自www.cnblogs.com/TorettoRui/p/10471541.html
今日推荐