[2018.11.03 T4] 7w523

本文深入解析了一款名为7王523的扑克牌游戏算法,该游戏支持2至5人参与,详细介绍了游戏规则、牌型大小、特殊出牌规则及积分计算方式。文章通过具体示例,展示了游戏流程,并提供了简化版问题解决思路。

7w523

题目描述

你玩过7王523吗?

这个扑克牌游戏可以 2~5 个人玩,最开始的时候,每人 555 张牌,庄家 666 张牌。庄家首先出牌,然后轮流出牌或不出牌。

第一个出牌的人可以打出任意牌,后面的人必须比前面的牌大才能出牌,且牌型必须要和前面人出牌的牌型一致或大于前面人的牌型。

牌的大小关系是 7>大王>小王>5>2>3>A>K>Q>J>10>9>8>6>4.不区分花色。

牌型有:对子(两张牌点数一样),炸弹(三张牌一样),氢弹(四张牌一样)

牌型的大小关系是:氢弹>炸弹>对子、单牌

另外还有一种特殊的出牌规则,如果有人出单牌,某人手上有一样点数的对子,则有对子的人可以忽略出牌顺序直接“碰”。在“碰”后,后面只有炸弹或氢弹可以出。(比如甲出单牌,后面只要有人有碰,一定是先满足碰)

当某个人打出的牌没人要时,本轮出牌结束,最后一个出牌的人取得本轮胜利,本轮出的牌就是胜利的人的积分,111 张牌记 111 分。再由胜利的人开始在剩余牌中按顺序拿几张牌和手上的牌凑够五张,接下来按顺序另外的人也从剩余牌中按顺序拿够 555 张。 如果牌库中牌的数量不够某个人拿够555张, 则停止拿牌环节。 如果发牌环节结束后某个玩家没有牌, 则整个游戏结束, 同时每个玩家的得分加上其手牌数量。

例如:

甲:2 4 4 6 K

乙:3 8 8 J 10

丙:K K 4 A A

剩余牌:7 7 2

假设出牌顺序是甲、乙、丙;

第一轮:甲先出 666,则乙可以出 JJJ101010333;丙如果不要,则甲可以出 222。这个时候没有人有牌能够大过 222,该轮出牌结束,甲获得胜利可以得到 333 个积分。接下来甲可以从剩余牌中获得 222777,而乙可以获得 222。丙没有出牌就不能拿。

第二轮:甲出 KKK,这个时候丙可以跳过乙优先进行“碰”,由于其他人没有炸弹,所以丙获得该轮胜利,得到 333 个积分。

为了简化问题,我们不考虑获胜的策略。也就是说轮到某人出牌,如果他手上的牌有大于前面人出的牌,则他就打出刚好大于前面的牌(有出必出),有“碰”必“碰”。 比如第一个人出 QQQ,第二个人牌是 K333K333K333,第三个人牌是 QQJQQJQQJ,则第三个人会先碰出来。因为对于第二个人来说他优先选择出 KKK,所以第三个人先碰。

每一轮第一个出牌的人,优先出手中最小的对子(为了尽可能让别人要不起, 7 除外),没有对子就出最小的单牌,没有单牌出对 7,没有对 7 出最小的炸弹,没有炸弹出氢弹。接下来轮流出牌,出的牌型必须和前面一至或大于前面的牌型,对子、炸弹、氢弹不能拆开出,出牌的原则的刚好大于前面的牌,如果没有大过前面的牌就 PASSPASSPASS,由后面一个人出牌。如果一个人出的牌其他人都 PASSPASSPASS,则本轮结束,最后一个出牌的为胜利者。

**注:**大王与小王不在一起算单牌,大王和小王在一起算炸弹(不能拆开),且是仅次于 777777777 的最大炸弹。任何氢弹都大于任何炸弹, 包括王炸。若牌库无牌, 且某玩家出牌后刚好打完, 这一回合仍然需要进行完才算游戏结束。

开始的时候,第一个人自动作为庄家先拿前面的 666 张牌,接下来玩家依次拿 555 张(若不够则停止拿牌环节)。当没有剩余的牌且发牌环节某个玩家手上没有牌,游戏结束,其他玩家手中的没有打出去的牌自动算为该玩家的积分。

输入输出格式
输入格式

一共 222 行;

第一行是一个整数 MMM 表示玩家人数,第二行是一个字符串表示初始牌的顺序,其中大王用GGG 表示,小王用 FFF 表示,10 用 XXX 表示。

输出格式

MMM 行,每行是一个整数,表示第 iii 个玩家最后获得的积分。

输入输出样例
输入样例#1:
3
25G36Q3885XKK4AA77A
输出样例#1:
13
2
4
样例解释

第一轮:第一个人出 6,然后第二个人出 X,第三个人不要,第一个人出 Q,第二个人出 3,第三个人不要,第一个人出 2,第二个人出 5、三个人不要,第一个人出 G,第二三个人不要,第一轮结束。第一个人获得积分 7,然后获得 77A 三张牌

第二轮:第一个人出 A, 第三个人有碰优先由第三个人碰,第三个人碰后,没有人有炸弹,该轮第三个人胜利。第三个人获得积分 3

第三轮:第三个人出对 K,第一个人出对 7,第一个人取得本轮胜利获得积分 4 第四轮:第一个人出 3,第二个人、第三个人 pass,第一个人取得胜利获得积分 1 第五轮:第一个人出 5,第一个人取得胜利获得积分 1。游戏结束。最后得分:

第一个人:13

第二个人:2

第三个人:4

【数据特点】

对于 100%数据,保证开始每个人至少有一张牌。一副牌中最多只有一张大王和小王,每个点数的牌张数不超过 4,所有字母均为大写,只有 J,Q,K,A,G,X,F,9,8,7,6,5,4,3,2 这些牌。

测试点玩家人数扑克牌总数特殊情况
12≤11\le 1111没有碰、炸弹、氢弹
22≤54\le 5454没有炸弹、氢弹
33≤16\le 1616
43≤54\le 5454没有碰
53≤54\le 5454没有炸弹、氢弹
64≤21\le 2121没有碰
74≤21\le 2121没有王炸
84≤54\le 5454没有碰、炸弹、氢弹
94≤54\le 5454没有炸弹、氢弹
104≤54\le 5454
题解

辣鸡大模拟,不过我大概是全网最短的代码:
1.png

代码
#include<cstdio>
#include<cstring>
#define put(pos,typ,siz) my[pos][siz]=0,cot[pos]-=typ,flag=1,last=(sd){pos,typ,siz}
const int N=15;
struct sd{int pos,typ,siz;}last;
int val[256],my[6][16],cot[6],sta[55],scr[6],top,n,flag;
char ch[55];
int kind[]={0,'4','6','8','9','X','J','Q','K','A','3','2','5','F','G','7'};
void getcard(int pos,int siz)
{
	for(;cot[pos]<siz&&top;++my[pos][sta[top--]],++cot[pos]);
	if(my[pos][13]&&my[pos][14])my[pos][13]=0,my[pos][14]=3;
}
void putcard(int pos)
{
	int typ=last.typ,siz=last.siz;
	if(!typ)
	{
		for(int i=1;i<N;++i)if(my[pos][i]==2){put(pos,2,i);return;}
		for(int i=1;i<=N;++i)if(my[pos][i]==1){put(pos,1,i);return;}
		if(my[pos][N]==2){put(pos,2,N);return;}
		for(int i=1;i<=N;++i)if(my[pos][i]==3){if(i==14)cot[pos]+=1;put(pos,3,i);return;}
		for(int i=1;i<=N;++i)if(my[pos][i]==4){put(pos,4,i);return;}
	}
	for(int i=1;i<=N;++i)if(my[pos][i]==typ&&i>siz){if(i==14&&typ==3)cot[pos]+=1;put(pos,typ,i);return;}
	for(int j=(typ==1?3:typ+1);j<=4;++j)for(int i=1;i<=N;++i)if(my[pos][i]==j){if(i==14&&j==3)cot[pos]+=1;put(pos,j,i);return;}
	flag=0;
}
bool peng(){for(int i=0;i<n;++i)if(last.typ==1&&my[i][last.siz]==2)return put(i,2,last.siz),last=(sd){i,3,-1},1;return 0;}
void game()
{
	for(int i=1;i<=N;++i)val[kind[i]]=i;
	for(int i=strlen(ch+1);i;--i)sta[++top]=val[ch[i]];
	getcard(0,6);for(int i=1;i<n;++i)getcard(i,5);
	for(int pos,cnt;;last.typ=0,scr[last.pos]+=cnt)
	{
		for(int i=0;i<n;++i)getcard((last.pos+i)%n,5);
		if(!top)for(int i=0;i<n;++i)if(!cot[i])return;
		putcard(last.pos),cnt=last.typ;if(last.typ==3&&last.siz==14)cnt-=1;
		if(peng())cnt+=2,pos=last.pos;
		for(pos=(last.pos+1)%n;pos!=last.pos;pos=(pos+1)%n,flag=0)
		{
			putcard(pos);
			if(flag){cnt+=last.typ;if(last.typ==3&&last.siz==14)cnt-=1;}
			if(peng())cnt+=2,pos=last.pos;
		}
	}
}
void in(){scanf("%d%s",&n,ch+1);}
void ac(){game();for(int i=0;i<n;++i)printf("%d\n",scr[i]+cot[i]);}
int main(){in(),ac();}
<think>我们正在解决一个关于Oracle JDBC驱动报错的问题。用户遇到了一个异常,关键错误信息是: "Exception in thread "main" java.sql.SQLException: ORA-06550: 第 1 行, 第 7 列: PLS-00201: 必须声明标识符 'FINDINFO'" 这个错误表明在调用存储过程时,数据库无法识别标识符'FINDINFO'。结合引用[3]中的代码示例,我们注意到调用存储过程的语句是: `CallableStatement call=conn.prepareCall("{? =call findClassinfoYearSal(?,?,?)}");` 但错误信息中的存储过程名是'FINDINFO',与代码中的'findClassinfoYearSal'不一致。这可能是导致错误的原因之一。 此外,引用[4]提到了一个XA错误,但当前问题似乎主要与存储过程调用有关。 因此,我们重点解决存储过程调用问题。 ### 步骤分析 1. **确认存储过程名称**:确保代码中调用的存储过程名称与数据库中的实际名称完全一致(包括大小写)。Oracle默认存储过程名称是大写的,除非创建时使用了双引号指定了大小写。 2. **检查存储过程是否存在**:在数据库中查询该存储过程是否存在。 3. **检查用户权限**:确保当前数据库用户有执行该存储过程的权限。 4. **检查连接用户**:在引用[3]中,连接使用的是SYS用户(以sysdba身份)。注意,SYS用户下的对象可能与其他用户不同,确保存储过程在正确的用户下。 5. **检查调用语法**:存储过程调用语法是否正确。 ### 具体解决方案 #### 1. 核对存储过程名称 - 在数据库中执行以下SQL查询,确认存储过程是否存在: ```sql SELECT object_name, object_type, status FROM all_objects WHERE object_type = 'PROCEDURE' AND object_name = 'FINDCLASSINFOYEARSAL'; -- 注意:Oracle默认大写 ``` 如果存储过程在另一个用户下,需要加上模式名前缀(如`scott.findClassinfoYearSal`)或者在调用时指定模式名。 #### 2. 检查权限 确保连接用户(这里是SYS)有执行权限。如果没有,需要授权: ```sql GRANT EXECUTE ON findClassinfoYearSal TO PUBLIC; -- 或者授权给特定用户 ``` #### 3. 修正调用代码 - 如果存储过程在另一个用户(模式)下,需要在存储过程名前加上模式名。例如,如果存储过程在用户`scott`下: ```java CallableStatement call = conn.prepareCall("{? = call scott.findClassinfoYearSal(?,?,?)}"); ``` - 注意:如果存储过程名称在数据库中是大小写敏感的(即创建时用了双引号),那么必须使用相同的大小写,并加上双引号。但通常不建议使用大小写敏感的名称。 #### 4. 测试存储过程 在SQL*Plus或SQL Developer中直接测试存储过程调用,确保其正常工作。例如: ```sql -- 假设存储过程有三个参数:两个OUT参数和一个IN参数 DECLARE v1 NUMBER; v2 VARCHAR2(100); v3 NUMBER; BEGIN v1 := findClassinfoYearSal('003', v2, v3); -- 注意:函数调用方式 -- 或者根据实际定义,可能是: -- findClassinfoYearSal('003', v2, v3); 如果是过程而不是函数 DBMS_OUTPUT.PUT_LINE(v1 || ', ' || v2 || ', ' || v3); END; ``` ### 代码修正示例 根据引用[3]中的代码,我们做如下调整(假设存储过程在SYS用户下,且名称是大写的): ```java // 注意:使用SYS用户时,通常不建议在SYS下创建用户对象,但这里假设已经存在 CallableStatement call = conn.prepareCall("{? = call FINDCLASSINFOYEARSAL(?,?,?)}"); // 或者如果存储过程是过程(没有返回值)而不是函数,则应该使用: // CallableStatement call = conn.prepareCall("{call FINDCLASSINFOYEARSAL(?,?,?)}"); ``` 注意:如果存储过程实际上是过程(没有返回值),那么调用语法应该是`{call procedure_name(...)}`,如果是函数,则使用`{? = call function_name(...)}`。这里需要根据存储过程的实际定义来确定。 ### 其他注意事项 - 如果存储过程在包中,那么调用时需要加上包名,如:`package_name.procedure_name`。 - 确保数据库连接正确,引用[3]中连接字符串为`"jdbc:oracle:thin:@localhost:1521:orcl"`,确保数据库监听正常。 ### 错误跟踪 错误堆栈中提到了`oracle.jdbc.driver.T4CTTIoer.processError`,这是JDBC驱动处理错误的内部方法。通常,我们需要根据驱动抛出的SQLException中的错误代码(如ORA-06550)来定位问题。 ### 总结 主要步骤: 1. 确认存储过程名称(包括大小写)和所属模式。 2. 检查当前连接用户是否有执行权限。 3. 在代码中正确调用(包括模式名前缀和调用语法)。 4. 测试存储过程在数据库环境中是否正常运行。 通过以上步骤,应该能够解决“标识符无效”的问题。 §§相关问题§§ 1. 如何查询Oracle中存储过程的定义? 2. 在Oracle中,如何授权执行存储过程的权限? 3. 使用JDBC调用存储过程时,如何处理OUT参数? 4. 如何在Oracle SQL Developer中调试存储过程? </think>### 解决 `oracle.jdbc.driver.T4CTTIoer11.processError` 处标识符无效错误的完整方案 该错误本质上是 **PLS-00201: 必须声明标识符** 问题,发生在调用存储过程时 Oracle 无法识别对象名称。以下是逐步解决方案: --- #### 1. **确认存储过程名称正确性** [^1][^3] - **核心问题**:错误 `PLS-00201` 直接表明 `FINDINFO` 未被数据库识别。 - **检查步骤**: ```sql -- 查询存储过程是否存在 SELECT object_name, object_type, status FROM all_objects WHERE object_type IN ('PROCEDURE','FUNCTION') AND object_name = UPPER('FINDINFO'); -- Oracle 默认大写 ``` - 若查询无结果,说明存储过程未创建或名称错误 - 若 `STATUS=INVALID`,需重新编译:`ALTER PROCEDURE FINDINFO COMPILE;` --- #### 2. **验证用户权限** [^1][^4] ```sql -- 检查当前用户是否有执行权限 SELECT * FROM all_tab_privs WHERE table_name = UPPER('FINDINFO'); -- 授权语句示例(需DBA执行) GRANT EXECUTE ON FINDINFO TO YOUR_USER; ``` --- #### 3. **修正 JDBC 调用代码** [^3] **错误模式**: ```java // 错误:未指定schema名称 CallableStatement call = conn.prepareCall("{? =call findClassinfoYearSal(?,?,?)}"); ``` **修正方案**: ```java // 方案1:添加schema前缀(推荐) CallableStatement call = conn.prepareCall("{? =call OWNER_SCHEMA.findClassinfoYearSal(?,?,?)}"); // 方案2:使用大写名称(Oracle默认) CallableStatement call = conn.prepareCall("{? =call FINDCLASSINFOYEARSAL(?,?,?)}"); // 方案3:添加双引号强制识别大小写(不推荐) CallableStatement call = conn.prepareCall("{? =call \"findClassinfoYearSal\"(?,?,?)}"); ``` --- #### 4. **检查数据库链路完整性** [^4] - **事务分支错误(XAErr)** 可能由网络中断或对象失效引起: ```sql -- 检查数据库链路状态 SELECT db_link, status FROM all_db_links; -- 重新编译失效对象 EXEC UTL_RECOMP.recomp_serial('OBJECT_SCHEMA'); ``` --- #### 5. **驱动版本兼容性验证** - 检查 Oracle JDBC 驱动与数据库版本匹配: | 数据库版本 | 推荐驱动 | |------------|-------------------------| | 11g | ojdbc6.jar (11.2.0.x) | | 12c/19c | ojdbc8.jar (19.x/21.x) | - Maven 配置示例: ```xml <!-- Oracle 19c 驱动 --> <dependency> <groupId>com.oracle.database.jdbc</groupId> <artifactId>ojdbc8</artifactId> <version>21.5.0.0</version> </dependency> ``` --- ### 故障排查流程图 ```mermaid graph TD A[报错 T4CTTIoer11.processError] --> B{检查错误代码} B -- PLS-00201 --> C[存储过程名称/权限问题] B -- XAErr -3 --> D[事务资源故障] C --> E[确认对象存在性] C --> F[验证执行权限] D --> G[检查DB链路状态] D --> H[重新编译失效对象] E --> I[修正JDBC调用语句] ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值