微软笔试3.25场讨论(附代码)
微软笔试题,根据讨论区内容写了一下前2题,第3题如果之后写出来会再更新
第1题:给定N个数,要求把N个数分为M组,每组内不能有重复的数,求每组最大的数和最小的数差,求所有的差求和最小的结果。
其实没有想到特别直观的方法,如果有比较好的思路请指导。
思路:先去除不可行情况,再最小到大排序、记录每个数的值和出现的次数,然后先从小到大简单贪心,再对后面出现的无法处理的情况贪心的做调整。
/* 1. 讨论去除不可行条件:1. N无法整除M,2. N中存在某个数字出现次数大于M 2. 对于可行条件,先排序,从小到大记录数字和其出现次数 3. 对于M个组,贪心生成每个组,从小到大选取还剩下的数字(每次选了数字,其剩余次数-1),具体为: 1. 如果选够了N/M个数字,这组先保留; 2. 如果没有选够,比如选了K个,还需要再选N/M - K个数字,则对于剩下的可选数字,一定和这一组的K个数字重复 所以,我们把剩下的可选数字从小到大与前面选好的组进行元素置换,对于数字V: 从下向上进行搜索(贪心),找到第一个不包含V的组,从后先前选取出第1个不在已选的K个数里的数字,置换这两个数 */ #include<iostream> #include<vector> #include<algorithm> using namespace std; struct prs { int val; int count; }; bool preprocess(vector<int> &sco, vector<prs> &v, int M) { sort(sco.begin(), sco.end()); prs ptmp; int i; ptmp.val = sco[0]-1; ptmp.count = 0; for(i = 0; i < sco.size(); i++) { if(ptmp.val != sco[i]) { if(i != 0) { if(ptmp.count > M) { return 0; } v.push_back(ptmp); } ptmp.val = sco[i]; ptmp.count = 1; } else { ptmp.count = ptmp.count + 1; } } v.push_back(ptmp); return 1; } bool find_val(vector<int> v, int val) { int l = 0, r = int(v.size() - 1); int mid; while(l <= r) { mid = (l+r)/2; if(v[mid] == val) { return 1; } else if(v[mid] > val){ r = mid - 1; } else { l = mid + 1; } } return 0; } void single_step_up(vector<vector<int> > &grps, vector<int> &tmp, int val) { int i, j; bool flag = 0; for(i = int(grps.size()-1); i >= 0 && flag == 0; i--) { if(find_val(grps[i], val) == 0) { for(j = int(grps[i].size() - 1); j >= 0 && flag == 0; j--) { if(find_val(tmp, grps[i][j]) == 0) { tmp.push_back(grps[i][j]); grps[i].erase(grps[i].begin()+j, grps[i].begin()+j+1); grps[i].push_back(val); flag = 1; } } } } return; } void update(vector<vector<int> > &grps, vector<int> &tmp, vector<prs> &v) { int i, cc = 0; int steps = int(grps[0].size() - tmp.size()); for(i = 0; i < steps; i++) { while(v[cc].count == 0) { cc++; } single_step_up(grps, tmp, v[cc].val); sort(tmp.begin(), tmp.end()); v[cc].count--; } return; } int bestgroup(vector<int> &sco, int M) { if(sco.size()%M != 0) { return -1; } vector<prs> v; bool flag = preprocess(sco, v, M); if(flag == 0) { return -1; } vector<vector<int> > grps; vector<int> tmp; int sum = 0; int i,j,cc; for(i = 0; i < M; i++) { cc = 0; j = 0; while(tmp.size() < sco.size()/M && cc < v.size()) { if(v[cc].count != 0) { tmp.push_back(v[cc].val); v[cc].count = v[cc].count - 1; } cc++; } if(tmp.size() != sco.size()/M) { update(grps, tmp, v); } grps.push_back(tmp); tmp.clear(); } for(i = 0; i < M; i++) { sum = sum + grps[i][sco.size()/M - 1] - grps[i][0]; } return sum; } int main() { int N, M; cin>>N>>M; int stmp; vector<int> sco; int i; for(i = 0; i < N; i++) { cin>>stmp; sco.push_back(stmp); } cout<<bestgroup(sco, M)<<endl; return 0; }
第2题:给定一个字符串,每次可以选择消除字符串中的一段回文子串,中间的子串消除后,剩下的左、右的子串会再拼起来,求最少次数课删除整个串。
思路:参考了讨论区代码,动态规划:
/* dp[i][j]表示第i~j位的字符串被消除需要的最小次数,则: 如果s[i] != s[j],那么dp[i][j] = min(dp[i][k] + dp[k+1][j]) 如果s[i] == s[j]:dp[i][j]还有一种额外的情况 = dp[i+1][j-1] ,可以理解为最后一次满足回文直接消除 也就是一定是可以分为两个分离的字符串段消除,求所有分离处理情况中最小的值 (根据不同题意有两种情况,即偶数长度的回文是否可以一次消除,会影响不同的初始化条件,本代码假设可以) */ #include<iostream> using namespace std; int minelim(string s1) { if(s1.length() <= 1) { return s1.length(); } int dp[s1.length()][s1.length()]; int i, j, k; for(i = 0; i < s1.length(); i++) { dp[i][i] = 1; } for(i = 0; i < s1.length()-1; i++) { if(s1[i] == s1[i+1]) { dp[i][i+1] = 1; } else { dp[i][i+1] = 2; } } for(i = 2; i < s1.length(); i++) { for(j = 0; i+j < s1.length(); j++) { if(s1[j] == s1[i+j]) { dp[j][i+j] = dp[j+1][i+j-1]; } else { dp[j][i+j] = i+1; } for(k = j; k < i+j; k++) { dp[j][i+j] = min(dp[j][i+j], dp[j][k] + dp[k+1][i+j]); } } } return dp[0][s1.length()-1]; } int main() { string s1; cin>>s1; cout<<minelim(s1)<<endl; }