当前位置:网站首页>【C】张梁计算器
【C】张梁计算器
2022-07-17 05:03:00 【日天君】
【项目介绍】
张梁是sp包的武将,其有一项技能“方统”,需要计算一系列牌的点数,使之和为36,然后便可对敌方造成大量伤害,具体信息可参考百科,想必点进来的都是懂张梁作为大数学家的含金量的,本项目就是为了研究一个算法,快速计算所有的炸人方案
什么是“大数学家”啊?
【代码展示】
引言:以大雁的这篇张梁视频跳转到1:00为例
张梁牌区所有点数为:2、12、13
“方”区所有点数为:6、7、12、12
一、函数介绍
- 全局变量
#define BOOM 36 //懂的都懂
// 牌区,空白处全为0
int PaiQ[20]={
2,12,13};
// “方”区
int Fang[20]={
6,7,12,12};
// 用于记录应当排除的“方”的下标,空白处赋为-1
int record[20];
int Fsum=0; //“方”区点数和
int Flen=0; //“方”区牌数
- 次要函数
/*将传入的数组初始化为value*/
void init(int* Array,int value)
{
for(int i=0;i<20;i++) Array[i]=value;
}
/*将数组Array视作队列压入新元素*/
void push(int* Array, int value, int newp)
{
// 令新元素newp替换第一个空白处(值为value)
for(int i=0;i<20;i++){
if(Array[i]==value){
Array[i]=newp;
break;
}
}
}
/*将数组Array视作队列弹出旧元素*/
void pop(int* Array,int oldp)
{
for(int i=0;i<20;i++){
// 找到与oldp同值的元素后,标记下标i
// 将后面所有元素依次往前推一位,直到遇到空白处
if(Array[i]==oldp){
for(;i<20;i++){
if(Array[i]==0) break;
Array[i]=Array[i+1];
}
break;
}
}
}
- 主要函数,各语句含义见注释
/*递归求解炸人方案,由总和倒推到单个元素(实际上推不到,最少必须有2张“方”才能炸人), 本质上是组合数的思想,fsum是子全“方”和,tag是即将减去的元素的下标,calC是计算组合数的意思*/
int calC(int fsum, int PaiQ_i, int tag){
fsum = fsum - Fang[tag];
// 这里与canBoom里判断的思想是一样的
if(fsum + PaiQ_i < BOOM) return 0;
else if(fsum + PaiQ_i == BOOM){
init(record,-1); //算出了炸人方案,表示要记录元素了,先初始化一下record
printf("可以炸人!方案为:");
return 1;
}
else if(fsum + PaiQ_i > BOOM){
while(++tag<Flen){
//++tag表示tag+1后的值,这里图省字了
if(calC(fsum, PaiQ_i, tag)==1){
// 因为我是递归地减去元素的,而要输出的是剩下的元素,
// 所以需要先记录每一步删去的是什么元素,再统一输出剩余的元素
push(record,-1,tag);
return 1;
}
}
}
}
/*输出解决方案*/
void printScheme(int PaiQ_i)
{
// 此时record里记录的是应当删去的元素下标
// 这里采取的方法是遍历record,将Fang(先用tmp复制)里对应下标的元素赋值为-1
// 然后遍历tmp,输出剩余元素
int tmp[20]={
0};
memcpy(tmp,Fang,20);
for(int i=0;i<20;i++){
if(record[i]==-1) break;
tmp[record[i]]=-1;
}
for(int i=0;i<20;i++){
if(tmp[i]==-1) continue;
if(tmp[i]==0) break;
printf("%d ",tmp[i]);
}
printf("+ 点数为%d的牌。\n",PaiQ_i);
}
/*判断是否可以炸人*/
void canBoom(){
init(record,-1); //初始化记录表
// 计算“方”区的点数和
for(int i=0;i<20;i++){
if(Fang[i]==0) break;
Fsum += Fang[i];
Flen++;
}
// 遍历牌区,抽出每一张牌,判断是否能炸人
for(int i=0;i<20;i++){
if(PaiQ[i]==0) break; //遇到牌区空白处,停止遍历
// 以下对应三种情况:
// 1.全部“方”和该牌点数的和都不够36,就不必算“方”的组合情况了
// 2.全部“方”和该牌点数的和正好为36,直接输出,不必再算
// 3.全部“方”和该牌点数的和超过36,需算“方”的组合情况,循环使用递归函数
if(Fsum + PaiQ[i] < BOOM){
printf("所有“方”与点数为%d的牌的和不满36,无炸人解;_;\n",PaiQ[i]);
}
else if(Fsum + PaiQ[i] == BOOM){
printf("所有“方”与点数为%d的牌的和正好为36,刚好炸^0^\n",PaiQ[i]);
}
else if(Fsum + PaiQ[i] > BOOM){
int tag=0; //下标
boolean ret=FALSE; //结果标记
while(tag<Flen){
//从tag=0开始,传入sumC的是原tag,算完后tag+1
if(calC(Fsum, PaiQ[i], tag++)==1){
ret=TRUE; //表示可以炸人,不输入失败语句
push(record,-1,tag-1);
printScheme(PaiQ[i]);
init(record,-1);
continue; //算出炸人方案就不必再往下递归了,之后向同级或往上递归
}
}
if(!ret) printf("很遗憾,点数为%d的牌没有炸人方案:(\n",PaiQ[i]);
}
}
}
二、主程序
int main(){
int newcard=0;
printf("分析炸人方案中...\n");
canBoom();
printf("计算结束!\n");
return 0;
}
三、效果展示


与视频中所展示的方案一样
再以视频中1:48为例:
张梁牌区:1、2、6、12、13
“方”区:6、8、9、10


得出的结论一致,而且额外算出一解
四、结论与问题
这里主要用的算法是循环套递归,目的是模拟组合数的计算,张梁的炸人规则要求对牌区(设为有 n 1 n_1 n1张牌)的每一张牌进行判断,也就是循环 n 1 n_1 n1次;
然后对方区(设有 n 2 n_2 n2张牌)计算所有组合数,也就是计算 2 n 2^n 2n-1 次
牌区: n 1 n_1 n1
方区: C n 2 1 + C n 2 2 + C n 2 3 + . . . + C n 2 n 2 = 2 n 2 − 1 C_{n_2}^{1}+C_{n_2}^{2}+C_{n_2}^{3}+...+C_{n_2}^{n_2}=2^{n_2}-1 Cn21+Cn22+Cn23+...+Cn2n2=2n2−1
总共计算次数: n 1 × ( 2 n − 1 ) n_1\times(2^n-1) n1×(2n−1)
总的来说,根据规则,时间复杂度“必然”是要达到 O( 2 n 2^n 2n)的,甚至是指数 × \times ×线性,可见复杂度非常高,当然也可以根据规则减少一些枚举操作:
- 牌区的牌 + + +若干方的和 ≤ \leq ≤ 36,不必深入计算
- 方区最少要抽出2张方,不必考虑1张方(牌点数最大只有13,1张方太少)的情况。
- 值相等的牌避免多算、值相等的方避免重复组合
(比较麻烦,没有研究)
由于本人才疏学浅,不太清楚有没有解决这种穷举组合数问题更好的算法,不知是否有高手能在评论区予以指点?不过我自认为我这种计算方法应该是比较简单的,因为契合组合数的计算方式,基本没有对同一情况的重复计算。
其他问题还有就是不太方便,需要将已有的牌区牌和方区的点数全部输入程序再计算,只能应用于事后分析,不能用于实战,博主日后还会研究如何在实战中实时地展示炸人方法,也就是类似于插件那样的程序或app。
边栏推荐
猜你喜欢

Basic operations of index library operation

mysql优化

一文了解配置中心

Emqx pressure test tread pit for your reference

DSL查询文档

shardingsphere内核原理
![[Lipschitz] simulation of Lipschitz Lipschitz exponent based on MATLAB](/img/72/c69ed6e5538169c362b7b4bab36d5e.png)
[Lipschitz] simulation of Lipschitz Lipschitz exponent based on MATLAB

浅聊全局过滤器

Notes on Advanced Mathematics: selected exercises of Wu Yue

Simple UI funny text conversion Emoji expression wechat applet supports sentence word conversion_ Source code
随机推荐
Construction and application of knowledge atlas de (V): knowledge reasoning
类对象自动注入属性操作工具
邮箱发送邮件(包含附件,网易、QQ)
Add SSL certificate for load balancing
fastjson、jackjson、gson区别和注意点
Tidb performance analysis and optimization
OLTP Load Performance Optimization Practice
DSL search results processing, including sorting, paging, highlighting
Desensitization field example
DSL搜索结果处理,包括排序,分页,高亮
拥抱声明式UI
Notes on Advanced Mathematics: selected exercises of Wu Yue
3.RestClient查询文档
DSL查询文档
CVE-2021-44228 Log4j 复现及原理
脱敏字段举例
NPM installation tutorial
Pingcap clinic data acquisition instructions
Simple UI funny text conversion Emoji expression wechat applet supports sentence word conversion_ Source code
Bank link No. cnasp & Query (II)