中级农民
- 积分
- 135
- 大米
- 颗
- 鳄梨
- 个
- 水井
- 尺
- 蓝莓
- 颗
- 萝卜
- 根
- 小米
- 粒
- 学分
- 个
- 注册时间
- 2017-2-1
- 最后登录
- 1970-1-1
|
本帖最后由 qich 于 2017-5-22 21:35 编辑
conlor 发表于 2017-5-22 11:46
谢谢你提供算法的解。假如牌的序列如下,你能算一下最后的解吗?
String cards="5,10,8,5,2,3,J,Q,9,A,K ...
看了一下楼主的求解过程,感觉楼主是看到了最开始的那个解法,那种解法是错误的,第二步用的是贪心,会陷入局部最优,没办法得到全局最优解。正解在第18垅:
(1)如果把这个问题简化一下,就是只发给一个人,那么发牌人就只有两种选择,也就是{发牌,弃牌}。然后考虑这种情况下找到最优解,那么到一次集齐21点的情况(一个轮次),也就是满21的最后一张牌,牌的序号越前越好,这样我们可以找到一个轮次下的最优解。这样n个轮次之后,就会发完所有的牌,得到这个简化问题的最优解。设最最优解为OnePerson21(v),v为能发的牌。
(2)将问题扩展一下,就是将扑克牌发给n个人(n>1)。在这种情况下,也就是先将扑克牌分成n份(v1,v2,v3....),然后问题也就转化成令OnePerson21(vn)的和最大,而原题即为n=4的情况。
当然这个算法效率是非常低的,所以必须要剪枝、记录中间值等方式来减少时间复杂度。
下面给出的是伪代码(是的我就是懒),因为实在太复杂了我手动计算是算不动的,我就不手动计算一次了(电脑应该也跑不出来):
int getN(vector<int>& cards, int begin, int n){
//返回得到N点时最后一张牌的位置,如果失败返回55
if(begin==cards.size())
return 55;
else if(cards[begin]==n)
return begin;
else if(cards[begin]==1 && n==11)
return begin;
else{
if(cards[begin]>n)
return getN(cards,begin+1,n);
else{
int index1 = getN(cards,begin+1,n-cards[begin]);
int index2 = getN(cards,begin+1,n);
int index3 = 55;
if(cards[begin]==1 && n>11)
index3 = getN(cards,begin+1,n-11);
return min(index1,index2,index3);
}
}
}
int get21(vector<int>& v){
int begin = 0;
int num = -1;
do{
num++;
begin = getN(v,begin,21);
}while(begin<55);
return num;
}
int OPT(map<int,int>& m, int i){
if(i>=m.size){
//分完四组牌
v1 = {x|map[x]==1};//所有分给第一个人的带序扑克牌
v2 = {x|map[x]==2};
v3 = {x|map[x]==3};
v4 = {x|map[x]==4};
return get21(v1)+get21(v2)+get21(v3)+get21(v4);
}else{
int num[4];
for(int t=0; t<4; t++){
map = t+1;
num[t][i] = OPT(m,i+1);
}
return max(num[0],num[1],num[2],num[3]);
}
}
注:OPT部分如果进行优化将重复出现的情况去除,可以将需要遍历的情况从4^52变成C(52,4)即270725,也一共需要进行27万次。(简单的优化过之后电脑应该勉强能够跑出来,如果追求更快的速度可以进一步优化
优化后的OPT函数伪代码:
bool getcard[5];
for(int i=0; i<5; i++){
getcard[i] = false;
}//getcard[4]为了简化代码,没有实际的意义
getcard[0] = true;
int OPT(map<int,int>& m, int i){
if(i>=m.size){
//分完四组牌
v1 = {x|map[x]==1};//所以分给第一个人的带序扑克牌
v2 = {x|map[x]==2};
v3 = {x|map[x]==3};
v4 = {x|map[x]==4};
return get21(v1)+get21(v2)+get21(v3)+get21(v4);
}else{
int num[4];
for(int t=0; t<4; t++){
if(getcard[i]){[/i][/i][/i][i][i][i] getcard[t+1] = true;
map[i] = t+1;
num[t][i] = OPT(m,i+1);[/i][/i][/i][/i][/i] //回头看时发现这里有问题,改不了代码,绝望,修改放楼下了[i][i][i][i][i]
}else{[/i][/i][/i][/i][/i][i][i][i][i] num[t][i] = 0; [/i][/i][/i][/i][/i][i][i][i][i][i]}[/i][/i][/i][/i][/i][i][i][i][i][i] }
return max(num[0],num[1],num[2],num[3]);
}
}[/i][/i][/i][/i][/i]
|
|