一道数学有关的贪心题
题意
给一个数组,任意连接其中的数,使得这个数是三的倍数且最大,以字符串输出最大的数,如果有前导0 需要去掉
数据范围
数组长度1e4 ,数组里的数都是[0,9]
分析
这题我上来就想反了,先说说错误的而且反了的贪心思路。
错误思路
三的倍数显然每位数的和是三的倍数,所以每个能被三整除的数都可以直接放入序列,于是需要合理选择3k+1,3k+2 的数,最后字符串排个序就好了。
合理选择的话,直接贪了余1 余2 尽可能匹配,剩下的三个一组,模拟很好写,成功WA了,反例[1,1,1,2] 。
于是考虑剩下的数里,余1 有x 个,余2 有y 个,不妨设x<y ,那么如果2x+(y−x)/3∗3>x/3∗3+y/3∗3 就按之前的思路,否则就直接将剩下的全部3 个一组匹配,代码也很好写。
这个时候突然意识到,如果两组数合理的构造一下,很有可能出现两种情况的构造长度相同但都不是最大的数,可能是在某一个特定位置开始二者自己匹配,但反正之前的代码都是复制粘贴就没管,果然被数据卡了。
正确思路
看到解析说删除而不是构造,恍然大悟。
因为在构造过程中很难判定“最大”,而删除的工作量要小的多。
如果原数组和本身就是3 的倍数,显然排序去0 就结束了;
考虑原数组和模3 余1 ,那么解决方法最好的显然是删除1 个3k+1 ;如果没有3k+1 ,说明一定可以删2 个3k+2 (一定有3n−1 个3k+2 才能形成模3 余1 ,删更多不值)。
考虑原数组和模3 余1 ,那么解决方法最好的显然是删除1 个3k+2 ;如果没有3k+2 ,说明一定可以删2 个3k+1 (一定有3n−1 个3k+1 才能形成模3 余2 ,删更多不值)。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| class Solution { public: string largestMultipleOfThree(vector<int>& digits) { string ans; int cnt[10]={0,0,0,0,0,0,0,0,0,0}; int sum=0; for(auto it:digits) { sum+=it; cnt[it]++; } if(sum%3==1) { bool flag=false; for(int i=1;i<9;i+=3) if(cnt[i]) { flag=true; cnt[i]--; break; } if(!flag) { for(int i=2;i<9;i+=3) { if(cnt[i]) { if(--cnt[i]&&!flag) { --cnt[i]; flag=true; } if(flag) break; flag=true; } } } } if(sum%3==2) { bool flag=false; for(int i=2;i<9;i+=3) if(cnt[i]) { flag=true; cnt[i]--; break; } if(!flag) { for(int i=1;i<9;i+=3) { if(cnt[i]) { if(--cnt[i]&&!flag) { --cnt[i]; flag=true; } if(flag) break; flag=true; } } } } for(int i=9;i>=0;i--) while(cnt[i]--) ans+=i+'0'; if(ans.length()&&ans[0]=='0') ans="0"; return ans; } };
|