1927
考虑初始状态下,前半边和后半边的总和差值x
那么B的目标就是让这个差值尽可能接近0
然后A的目标是让这个差值远离0
然后还要知道当前填的这个数,是在前半边还是后半边
对于差值为0的必胜态,往上延申就是只剩一个问号时,能否通过那个问号导向差值为0
还是先从正向DFS考虑,初始时前后差值为a,然后记录问号所在的下标数组
dfs返回的,应该是当前状态下,能否导向差值不为0
然后每层的状态就是?数组和目前差值,以及当前的执行人
对于最底层的叶子节点,对于A,无论如何选都可以导向,直接返回true
对于B,如果差值为正,且最后的问号在前面,那么一定为true
如果差值为正,问号在后面,但差值大于9,也一定为true,否则为false
对于差值为负的情况,则完全对称
然后是遍历与合并
对每个还没确定的问号,遍历从零到9
如果当前是A在执棋,那么只要有一个是true,那就返回true,否则false
如果是B在执棋,只要有一个false,那就返回false,否则true

更自然的底层节点描述


对比



cpp
class Solution {
public:
bool sumGame(string num) {
int n=num.size(),count=0;
vector<bool>vis(n,false);//cur为true则为A执棋,否则为B
function<bool(int,int,bool)>dfs=[&](int delta,int cnt,bool cur)->bool{
// if(cnt==1){
// if(cur){return true;}
// for(int i=0;i<n;i++){
// if(!vis[i]){
// if(i<n/2&&delta>-9){return false;}
// if(i>=n/2&&delta<9){return false;}
// return true;
// }
// }
// }
if(cnt==0){
return delta!=0;
}
bool res;
for(int i=0;i<n;i++){
if(!vis[i]){
vis[i]=true;
for(int j=0;j<=9;j++){
//cnt++;
if(i<n/2){res=dfs(delta+j,cnt-1,!cur);}
else{res=dfs(delta-j,cnt-1,!cur);}
if(res&&cur){break;}
if(!res&&!cur){break;}
}
vis[i]=false;
if(res&&cur){break;}
if(!res&&!cur){break;}
}
}
return res;
};
int tmp=0,i=0;
for(;i<n/2;i++){
if(num[i]!='?'){
vis[i]=true;
tmp+=(num[i]-'0');
}else{
count++;
}
}
for(;i<n;i++){
if(num[i]!='?'){
vis[i]=true;
tmp-=(num[i]-'0');
}else{
count++;
}
}
return dfs(tmp,count,true);
}
};
这版 DFS 的逻辑是正确的,但会超时。
因为 ? 最多可以很多,DFS 枚举量爆炸。
正解

B的获胜条件为什么是"2 * delta == 9 * (rightQ - leftQ)"?即这个等式是怎么来的







如果问号在两侧,B自然可以抵消A的影响,但也可以超过A配的数来进一步抵消差值啊;而且在同侧时,B为什么要选择9-x,一定要配出9呢?
没理解









