惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
GbyAI
GbyAI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Apple Machine Learning Research
Apple Machine Learning Research
大猫的无限游戏
大猫的无限游戏
阮一峰的网络日志
阮一峰的网络日志
Last Week in AI
Last Week in AI
V
Visual Studio Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
The Cloudflare Blog
博客园 - 【当耐特】
博客园 - 叶小钗
量子位
博客园 - 聂微东
S
SegmentFault 最新的问题
美团技术团队
Hugging Face - Blog
Hugging Face - Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
月光博客
月光博客
宝玉的分享
宝玉的分享
小众软件
小众软件
罗磊的独立博客
有赞技术团队
有赞技术团队
Stack Overflow Blog
Stack Overflow Blog

OhYee 博客

小鹏辅助驾驶测评|OhYee 博客 小鹏非支持手机开启自动解锁|OhYee 博客 使用函数计算实现 301 重定向|OhYee 博客 针对 HTML 内容使用 Ant Design 图片弹框|OhYee 博客 博客进程泄露及僵尸进程解决|OhYee 博客 蓝易云服务器体验|OhYee 博客 SSH 调起本地 VSCode|OhYee 博客 【2022 秋招内推】阿里云后端研发工程师|OhYee 博客 使用函数计算获取 IP 地址信息|OhYee 博客 正确获取客户端 IP/HTTP Header 也可能重复|OhYee 博客 评测 Oculus Quest2 及 BigScreen|OhYee 博客 NextJS 热重载保留状态|OhYee 博客 如何优雅地贴 gist 代码|OhYee 博客 Linux 精细化文件权限|OhYee 博客 VSCode 容器开发环境|OhYee 博客 Clash 的不兼容更新排查|OhYee 博客 Zeek 导出 PCAP|OhYee 博客 记一次 ssh 配置问题|OhYee 博客 Git Commit 规范化工具|OhYee 博客 谈谈《星之卡比-探索发现》|OhYee 博客 VSCode 快捷键绑定 Shell 命令|OhYee 博客 ASN.1 语法及 X.509 证书格式解析解析|OhYee 博客 腾讯企业邮箱忽略 MX 记录发信|OhYee 博客 Chrome/Edge 标签组插件|OhYee 博客 【应届内推】阿里云后端研发工程师|OhYee 博客 损坏的 Typecho 备份处理为 JSON|OhYee 博客 VS Code VIM 插件高效使用|OhYee 博客 SSH 正反向代理|OhYee 博客 Let's Encrypt 根证书过期引发的问题|OhYee 博客 OpenWRT 忽略内核依赖|OhYee 博客
HDU 5937.Equation(2016 CCPC 杭州 E)|OhYee 博客
2016-11-28 · via OhYee 博客

题目

Description

Little Ruins is a studious boy, recently he learned addition operation! He was rewarded some number bricks of 1 to 9 and infinity bricks of addition mark '+' and equal mark '='.

Now little Ruins is puzzled by those bricks because he wants to put those bricks into as many different addition equations form x+y=z as possible. Each brick can be used at most once and x, y, z are one digit integer.

As Ruins is a beginer of addition operation, x, y and z will be single digit number.

Two addition equations are different if any number of x, y and z is different.

Please help little Ruins to calculate the maximum number of different addition equations.

Input

First line contains an integer T, which indicates the number of test cases.

Every test case contains one line with nine integers, the ith integer indicates the number of bricks of i.

Limits
1≤T≤30
0≤bricks number of each type≤100

Output

For every test case, you should output 'Case #x: y', where x indicates the case number and counts from 1 and y is the result.

Sample Input

3
1 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 2
0 3 3 0 3 0 0 0 0

Sample Output

Case #1: 2
Case #2: 6
Case #3: 2

题解

{% cq %}
有技巧的暴力,就不算暴力了
{% endcq %}

历时超长的一道题,如果不是知道思路没问题,估计中途就想办法用其他方法了

题目的意思就是给任意数目的 1-9 9个数字,使尽可能多的凑 a+b=c 的式子
其中 a、b、c 必须是 1-9 的数字,并且 a+b=cb+a=c 被看成是两个不同的式子

首先,很容易发现,总共只有 36 种选择,因此可以尝试使用搜索解决

不考虑时间,思路如下:
暴力 DFS 搜索,遍历所有的情况,判断所有情况中递归的最大深度
时间复杂度 O(236)

优化:
显然,这样写超时会非常严重

  1. 对于遍历次数的优化
    仔细跟着思路模拟一下,会发现,对于 a+b=cb+a=c 的情况,他们属于可以把 a+b=c 选择两次的情况
    而直接遍历 36 种可能显然把 a+b=c 只选一次的情况 遍历了两次
    因此,这里可以先标记下然后再进行判断,如果 a+b=c 不在 DFS 递归的栈里(已经遍历过只选一次的分支),那么不再遍历 b+a=c
  2. 剪枝
  • 当深度大于等于 36 的时候,显然已经找到答案了,可以直接返回
  • 如果把现在所有的数字除以 3 (每个式子有 3 个数字) 再加上已经遍历的深度都不可能比已有答案大,显然可以直接返回
  • 如果把当前已经遍历的深度(已选择的式子数目),和剩下能遍历的深度加起来都没有已有答案大,可以直接返回
  1. 预处理
    预处理到每个深度,其以后所有式子需要的每个数字的个数.当发现数字足够时,直接返回返回答案

这样处理后,不管是数字总体比较少(直接爆搜也很快)还是数字数目比较多(由于数字数目多,所以预处理可以解决许多情况,并且剪枝可以进一步减少搜索量),都可以较快地搜到答案

对于这道题,其数据量处于爆搜能够解决的边缘范围,因此采用一定的搜索技巧可以达到时间要求
而搜索的技巧就是上面的三种
而其中剪枝分为 可行性剪枝 和 最优性剪枝

注释比较详细

代码

//#define debug
//#include <ctime>

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;

int Left[37][10];
int lis[36][3];

//记录对应的等式是否在这支dfs树枝上
//0 (1,2) (1,3) (1,4) (1,5) (1,6) (1,7) (1,8)
//7 (2,3) (2,4) (2,5) (2,6) (2,7) 
//12 (3,4) (3,5) (3,6)
//15 (4,5)
bool vis[16];
#define Flag(a,b) (vis[(8*(a-1)-(a-1)*(a-1))+(b-a-1)])

int num[10];
int deep;

void Debug(int n, int m) {
    return;
    for (int i = 0; i < n; i++)
        cout << " ";
    cout<<n<<" "<<lis[m][0]<<"+"<<lis[m][1]<<"="<<lis[m][2];
    cout<<"\t\t\t\t\t deep:"<<n<<" p:"<<m<<"\t";
    for(int i=1;i<=9;i++)
        cout<<num[i]<<" ";
    cout<<endl;
}

void dfs(int p, int d, int cnt) {
    // 剪枝
    if (d == 36 || deep ==36) return;// 不可能有更优情况
    if (cnt/3+d <= deep) return;// 所有数字全用上也无法最优  
    if (d+36-p <= deep) return;//剩下的组合都能用上都不可能更优

    //如果有预处理能解决的问题,直接返回答案
    bool Ok = true;
    for(int i=1;i<=9;i++){
        if(num[i] < Left[p][i]){
            Ok = false;
            break;
        }
    }
    if(Ok){
        deep = max(deep,d+36-p);
        return;
    }
    
    deep = max(d,deep);

    for (int i = p; i < 36; i++) {
        num[lis[i][0]]--;
        num[lis[i][1]]--;
        num[lis[i][2]]--;
        if (num[lis[i][0]]>=0 && num[lis[i][1]]>=0 && num[lis[i][2]]>=0){
            Debug(d + 1, i);
            if(lis[i][0] < lis[i][1]){
                Flag(lis[i][0],lis[i][1]) = true;
                dfs(i + 1, d + 1, cnt - 3);
                Flag(lis[i][0],lis[i][1]) = false;
            }else{
                if(lis[i][0] == lis[i][1] || 
                    (lis[i][0] > lis[i][1] && Flag(lis[i][1],lis[i][0]) == true)
                )
                    dfs(i + 1, d + 1, cnt - 3);
            }  
        }
        num[lis[i][0]]++;
        num[lis[i][1]]++;
        num[lis[i][2]]++;
    }
}

//预处理
void Pre(){
    //init 
    memset(vis,false,sizeof(vis));

    //预处理表
    int pos = 0;
    for(int i=1;i<=9;i++)
        for(int j=1;j<=9;j++)
            if(i+j<10){
                lis[pos][0] = i;
                lis[pos][1] = j;
                lis[pos++][2] = i+j;
            }else{
                break;
            }

    //预处理结果
    memset(Left[36],0,sizeof(Left[36]));
    for(int i=35;i>=0;i--){
        for(int j=1;j<=9;j++){
            Left[i][j] = Left[i+1][j];
        }
        for(int j=0;j<3;j++)
            Left[i][lis[i][j]]++;
    }

    //预处理显示
    /*
        cout<<"lis"<<endl;
        for(int i=0;i<pos;i++){
            cout<<i<<": "<<lis[i][0]<<" + "<<lis[i][1]<<" = "<<lis[i][2]<<endl;
        } 
        cout<<endl;
        cout<<"Left"<<endl;
        for(int i=0;i<pos;i++){
            cout<<i<<": ";
            for(int j=1;j<=9;j++)
                cout<<Left[i][j]<<" ";
            cout<<endl;
        }
        cout<<"==============="<<endl;
    //*/
}

int main() {
    #ifdef debug
    freopen("in.txt", "r", stdin);
    int START = clock();
    #endif

    cin.tie(0);
    cin.sync_with_stdio(false);

    Pre();

    int T;
    cin >> T;
    for (int kase = 1; kase <= T; kase++) {
        int cnt = 0;
        for (int i = 1; i <= 9; i++) {
            cin >> num[i];
            num[i]=min(num[i],17-i);
            cnt += num[i];
        }
        deep = 0;
        dfs(0, 0, cnt);
        cout << "Case #" << kase << ": " << deep << endl;
    }

    #ifdef debug
    printf("Time:%.3lfs\n", double(clock() - START) / CLOCKS_PER_SEC);
    #endif

    return 0;
}