codeforces gym-101673 Twenty Four, Again 24点,枚举表达式树过题

本文介绍了一种解决经典24点游戏的算法,通过暴力枚举数的顺序、运算符类型及表达式结构来寻找构成24点的最低代价方案。文中详细解释了如何计算增加括号和交换数顺序的代价,并给出了具体实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

题目链接

题意

给出4个数字,让你任意指定运算符(3个)、增加括号、交换数,问组成24点的代价最小是多少。

增加括号:代价为1。

交换数的顺序:代价为2。

题解

方法就是暴力枚举,我们可以先枚举数的顺序( 4! 4 ! 种可能性),再枚举运算符类型( 43 4 3 种可能性),再枚举表达式树的结构( 5 5 种情况,计算可以使用卡特兰数计算)。
然后在把枚举的数的顺序、运算符填入表达式树中去。

至于填括号的操作,我们在一颗完整的表达式树构造完成后,可以判断/节点下是否有直接有 + + 、 − ,有几个 + + 、 − 就要增加几对括号。

这样的话费用就计算出来了,下面只需要按照后序遍历计算表达式树,判断结果是否为24就可以了。

注意如果不能整除的话,要提前终止计算,返回当前情况不满足。

在我写这道题的时候,排列、排列的代价、表达式树的结构种类我都手动计算出来了,所以在这道题里面,显得比较暴力。

代码

#include <iostream>
#include <algorithm>
using namespace std;
int a[4],na[4];
int ps[][4] = {
                {0,1,2,3},{0,1,3,2},{0,2,1,3},{0,2,3,1},{0,3,1,2},{0,3,2,1},
                {1,0,2,3},{1,0,3,2},{1,2,0,3},{1,2,3,0},{1,3,0,2},{1,3,2,0},
                {2,0,1,3},{2,0,3,1},{2,1,0,3},{2,1,3,0},{2,3,0,1},{2,3,1,0},
                {3,0,1,2},{3,0,2,1},{3,1,0,2},{3,1,2,0},{3,2,0,1},{3,2,1,0}
        };
int cp[] = {0,1,1,2,2,3,1,2,2,3,3,4,2,3,3,4,4,5,3,4,4,5,5,6};
struct node{
    int num,lson,rson;
}ns[100];
void init(){
    ns[1].lson=2;ns[1].rson=3;ns[2].lson=4;ns[2].rson=5;ns[3].lson=6;ns[3].rson=7;
    ns[8].lson=9;ns[8].rson=10;ns[9].lson=11;ns[9].rson=12;ns[11].lson=13;ns[11].rson=14;
    ns[15].lson=16;ns[15].rson=17;ns[16].lson=18;ns[16].rson=19;ns[19].lson=20;ns[19].rson=21;
    ns[22].lson=23;ns[22].rson=24;ns[24].lson=25;ns[24].rson=26;ns[25].lson=27;ns[25].rson=28;
    ns[29].lson=30;ns[29].rson=31;ns[31].lson=32;ns[31].rson=33;ns[33].lson=34;ns[33].rson=35;
}
void tree_fill(int rt,int i,int& idx,int& op){
    if(ns[rt].lson == 0 && ns[rt].rson == 0){
        ns[rt].num = a[ps[i][idx++]];
        return ;
    }
    tree_fill(ns[rt].lson,i,idx,op);
    ns[rt].num = -(op%4)*1000 - 1000;
    op /= 4;
    tree_fill(ns[rt].rson,i,idx,op);
}
int calc(int rt,int &cost){
    if(ns[rt].lson == 0 && ns[rt].rson == 0) return ns[rt].num;
    if(ns[rt].num == -3000 || ns[rt].num == -4000){
        if(ns[ns[rt].lson].num == -1000 || ns[ns[rt].lson].num == -2000) cost ++;
        if(ns[ns[rt].rson].num == -1000 || ns[ns[rt].rson].num == -2000) cost ++;
    }
    int l = calc(ns[rt].lson,cost);
    int r = calc(ns[rt].rson,cost);
    if(l == -1000 || r == -1000) return -1000;
    switch(ns[rt].num){
        case -1000:return l+r;
        case -2000:return l-r;
        case -3000:return l*r;
        case -4000:
                if(r == 0 || l % r != 0) return -1000;
                return l / r;
    }
}
int mi = 1000;
int main(){
    init();
    cin>>a[0]>>a[1]>>a[2]>>a[3];
    for(int t = 0;t < 5;++t)
    for(int i = 0;i < 24;++i){
        for(int j = 0;j < 64;++j){
            int idx = 0,op = j,cost = 2*cp[i];
            tree_fill(t*7+1,i,idx,op);
            int res = calc(t*7+1,cost);
            if(res == 24) mi = min(mi,cost);
        }
    }
    if(mi == 1000) puts("impossible");
    else printf("%d\n",mi);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值