题目链接:
1. 先对等式进行移项,假设原来为1 + 2 = 4 - 5 + 6,那么移动之后就变为了 1 + 2 - 4 + 5 + 6 = 0. 并计算出所有数字之和sum,和等式左边中的正数个数plusNum
2. 然后就演变成了“从n个数中选取总和为sum/2的plusNum个数字组成正数部分”,因为如果等式成立,那么所有正数之和与所有负数之和的绝对值一定是相等的。正样的话,对于每个数,要么取,要么不取,复杂度就变为了 2^16
3. 减枝: 1) 每次递归时,如果当前值加上要选的那个数之和大于sum/2,则不选
2) 如果所有数字之和为奇数,那么直接no solution。这个优化相当大,直接从1.9s降到了0.08s。不过rank第一的神牛的0.02s真不知道怎么跑出来的 Orz..
#include<cstdio> #include<cstring> const int maxn = 1000; char str[10000]; char sigma[maxn]; bool vis[maxn]; int num[maxn], ans[maxn], n, idx; int plus[maxn], leftNum, plusNum, sum; inline void read(){ idx = 1; sscanf(str, "%d", &num[0]); sum = num[0]; int len = strlen(str); for(int i=1; i<len; ++i){ if(str[i]=='+' || str[i]=='-' || str[i]=='=') { if(str[i] == '=') leftNum = idx; sigma[idx] = str[i]; sscanf(str+i+1, "%d", &num[idx]); sum += num[idx++]; } } // 把等式全部移到左边后,计算有多少个加号 plusNum = 1; for(int i=1; i<idx; ++i){ if(i<leftNum && sigma[i] == '+'){ ++plusNum; } else if(i>leftNum && sigma[i] == '-') ++plusNum; } } bool dfs(int cur, int pos, int tot){ if(plusNum-cur > idx-pos) return false; if(cur == plusNum){ if(tot*2 == sum) return true; return false; } if(pos<idx && (tot+num[pos])*2 <= sum){ vis[pos] = true; plus[cur] = num[pos]; if(dfs(cur+1, pos+1, tot+num[pos])) return true; } vis[pos] = false; if(pos<idx && dfs(cur, pos+1, tot)) return true; return false; } inline void solve(){ //注意减枝!如果sum为奇数那么直接输出no solution memset(vis, 0, sizeof(vis)); if((sum&1)==0 && dfs(0, 0, 0)){ int id=1; ans[0] = plus[0]; for(int i=1; i<idx; ++i){ if(i<leftNum && sigma[i]=='+') { ans[i] = plus[id++]; } else if(i>leftNum && sigma[i]=='-'){ ans[i] = plus[id++]; } } int k=1; for(int i=0; i<idx; ++i)if(!vis[i]){ while(k<leftNum && sigma[k]=='+' && k<idx) ++k; while(k>leftNum && sigma[k]=='-' && k<idx) ++k; ans[k++] = num[i]; } printf("%d", ans[0]); for(int i=1; i<idx; ++i) if(i==leftNum) printf(" = %d",ans[i]); else printf(" %c %d", sigma[i], ans[i]); puts(""); }else{ puts("no solution"); } } int main(){ while(gets(str)){ read(); solve(); } return 0; }