我制作了一个程序来解决ACM中的this problem问题。
火柴棍是表示数字的理想工具。使用火柴棍表示十位小数的常用方法如下:
这与普通闹钟上的数字显示方式相同。使用给定数量的火柴棍,您可以生成多种数字。我们想知道使用所有火柴棍可以创建的最小和最大数字是什么。
输入
在第一行上有一个正数:测试用例的数量,最多为100.每个测试用例之后:
一行整数n(2≤n≤100):你有的火柴数。 输出
每个测试用例:
您可以创建的最小和最大数字的一行,由单个空格分隔。两个数字都应该是正数并且不包含前导零。 样本输入
4 3 6 7 15 样本输出
7 7 6 111 8 711 108 7111111
问题在于,对于100个火柴棍而言,解决它的速度太慢了。搜索树太大了,无法强制它。
以下是前10个的结果:
2:1 1
3:7 7
4:4 11
5:2 71
6:6 111
7:8 711
8:10 1111
9:18 7111
10:22 11111
最大值的模式很简单,但我没有看到最小值的快捷方式。有人可以建议一个更好的方法来解决这个问题吗?这是我使用的代码:
#include <iostream>
#include <string>
using namespace std;
#define MAX 20 //should be 100
//match[i] contains number of matches needed to form i
int match[] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};
string mi[MAX+1], ma[MAX+1];
char curr[MAX+1] = "";
//compare numbers saved as strings
int mycmp(string s1, string s2)
{
int n = (int)s1.length();
int m = (int)s2.length();
if (n != m)
return n - m;
else
return s1.compare(s2);
}
//i is the current digit, used are the number of matchsticks so far
void fill(int i, int used)
{
//check for smaller and bigger values
if (mycmp(curr, mi[used]) < 0) mi[used] = curr;
if (mycmp(curr, ma[used]) > 0) ma[used] = curr;
//recurse further, don't start numbers with a zero
for (int a = i ? '0' : '1'; a <= '9'; a++) {
int next = used + match[a-'0'];
if (next <= MAX) {
curr[i] = a;
curr[i+1] = '\0';
fill(i + 1, next);
}
}
}
int main()
{
//initialise
for (int i = 0; i <= MAX; i++) {
mi[i] = string(MAX, '9');
ma[i] = "0";
}
//precalculate the values
fill(0, 0);
int n;
cin >> n;
//print those that were asked
while (n--) {
int num;
cin >> num;
cout << mi[num] << " " << ma[num] << endl;
}
return 0;
}
编辑:我最终使用了动态编程解决方案。我之前用dp尝试了它,但我正在弄乱二维状态数组。这里的解决方案要好得多。谢谢!
答案 0 :(得分:4)
您可以使用动态编程解决方案。
假设您有n个匹配项,并且您知道如何解决所有n-k
个匹配项的问题(最小数量),其中k
获取与匹配数相对应的所有值每个数字使用(2为1,5为3,等等)
然后递归地导出解决方案。假设你用一个(在最不重要的位置)结束你的号码,那么通过写(best solution with n-2 matches) 1
获得最佳解决方案。假设你用两个结束它,最好的解决方案是(best solution with n-5 matches) 2
。等等 ;最终你可以比较这十个数字,并选出最好的数字。
现在,您所要做的就是为所有n
设置比输入更小的最佳解决方案,递归。
编辑:如果以直接的方式实现此算法,最终会出现指数复杂性。这里的诀窍是要注意,如果你的核心函数MinimumGivenNMatches
只有一个参数n
。因此,你最终会用相同的值多次调用它。
为了使复杂性变得线性,您只需要使用辅助数组为每个n
记忆(即记住)解决方案。
答案 1 :(得分:2)
为了找到结果:
应选择每个数字,以便存在剩余数字的解决方案。 每个数字需要2到7个匹配。所以你必须选择最小的第N个“顶部”数字,留下2 *(N-1)和7 *(N-1)之间的剩余匹配数。
不要忘记在搜索结果的最高位数时必须排除0。
作为旁注,使该算法有效的一个原因是每个(匹配)值在2和7之间至少有一个相应的数字。
编辑: 10场比赛的示例 10场比赛 - &gt; 2位数 顶部数字的可接受匹配数量= 3到7之间 最小的数字,需要3到7个匹配 - &gt; 2(需要5场比赛),0被排除在外 选择第一个数字= 2
剩余5场比赛 - &gt;
第二个(在这种情况下是最后一个)数字的可接受的匹配数量=正好5个
最小的数字,需要5个匹配 - &gt; 2
选择第二个数字= 2
结果= 22。
编辑此问题的代码
#include <iostream>
#include <vector>
std::vector<int> nbMatchesForDigit;
long long minNumberForMatches(unsigned int nbMatches)
{
int nbMaxMatchesForOneDigit = 7;
int nbMinMatchesForOneDigit = 2;
int remainingMatches = nbMatches;
int nbDigits = 1 + nbMatches / nbMaxMatchesForOneDigit;
long long result = 0;
for (int idDigit = 0 ; idDigit < nbDigits ; ++idDigit )
{
int minMatchesToUse = std::max(nbMinMatchesForOneDigit, remainingMatches - nbMaxMatchesForOneDigit * (nbDigits - 1 - idDigit));
int maxMatchesToUse = std::min(nbMaxMatchesForOneDigit, remainingMatches - nbMinMatchesForOneDigit * (nbDigits - 1 - idDigit));
for (int digit = idDigit > 0 ? 0 : 1 ; digit <= 9 ; ++digit )
{
if( nbMatchesForDigit[digit] >= minMatchesToUse &&
nbMatchesForDigit[digit] <= maxMatchesToUse )
{
result = result * 10 + digit;
remainingMatches -= nbMatchesForDigit[digit];
break;
}
}
}
return result;
}
int main()
{
nbMatchesForDigit.push_back(6);
nbMatchesForDigit.push_back(2);
nbMatchesForDigit.push_back(5);
nbMatchesForDigit.push_back(5);
nbMatchesForDigit.push_back(4);
nbMatchesForDigit.push_back(5);
nbMatchesForDigit.push_back(6);
nbMatchesForDigit.push_back(3);
nbMatchesForDigit.push_back(7);
nbMatchesForDigit.push_back(6);
for( int i = 2 ; i <= 100 ; ++i )
{
std::cout << i << " " << minNumberForMatches(i) << std::endl;
}
}
答案 2 :(得分:2)
使用dynamic programming代替递归。存储计算值并重新使用它们要快得多。实际上,它将指数运行时间转换为多项式运算时间。
基本思想是拥有一个数组min
,它可以跟踪使用n
个火柴棍完成的最小数量。所以
min[0] = ""
min[1] = infinity
min[2] = "1"
min[3] = min("1+"min[3-2],"7")
min[4] = min("1"+min[4-2],"7"+min[4-3])
etc
答案 3 :(得分:1)
对于最小值,请注意,由于不允许前导零,因此您希望最小化位数。最小位数为ceil(n/7)
。
然后很容易计算必须在前导数字中使用的最小数量的火柴棍,从而得到前导数字的最小可能值。
答案 4 :(得分:0)
我能够用int nofDigits = n/7+ ((0 == n%7) ? 0 : 1)
解决问题,其中d是位数。我们的想法是,首先我们计算所需最小数字的最小位数。这可以通过n
计算,其中nofDigits
是火柴棍的数量。现在创建一个void minNumWithNMatches_no_dp (int n) {
if (n < 2) return ;
int digits[] = {-1, -1, 1, 7, 4, 2, 0, 8};
int nofDigits = n/7+ ((0 == n%7) ? 0 : 1);
int *matchesArr = new int [nofDigits];
int tmp = n;
for (int i = 0; i < nofDigits; ++i) {
matchesArr[i] = tmp/7 ? 7 : tmp%7;
tmp -= matchesArr[i];
}
if (nofDigits > 1)
{
switch (matchesArr[nofDigits - 1]) {
case 1:
case 4:
{
--matchesArr[nofDigits - 2];
++matchesArr[nofDigits - 1];
break;
}
case 3:
{
if (nofDigits > 2) {
--matchesArr[nofDigits - 2];
--matchesArr[nofDigits - 3];
} else {
matchesArr[nofDigits - 2] -= 2;
}
matchesArr[nofDigits - 1] += 2;
break;
}
case 2:
case 5:
case 6:
case 7:
default:
{
;
}
}
}
for (int i = nofDigits - 1; i >= 0; --i) {
int digit = digits[matchesArr[i]];
if ((i == nofDigits - 1) && (digit == 0)) digit = 6;
std::cout<< digit;
}
}
数组。现在我们开始从最低有效数字到最大有效数字(MSD)之前的一个数字填充最大可能的火柴(7),最后将所有剩余火柴杆分配到最高有效数字(MSD)。现在有三种改进的可能性,具体取决于MSD的火柴杆数量:
如果MSD的火柴数为1,那么我们可以通过从其相邻数字借一个火柴棍来使其成为2。这将使相邻数字6的火柴杆数量相当于0
2d如果MSD的火柴数为4,那么也与之前的情况相同,我们可以将MSD的火柴数增加到5,相当于2
如果MSD的火柴数是3,那么我们必须看看总数是否大于2然后我们可以从MSD的两个相邻数字递减1,否则我们将减少一个相邻数字两次并增加MSD的火柴数量为2
最后,遍历数组并用相应的数字替换火柴棍的数量。
完整的计划:
function isPushSupported() {
var isSupported = false;
if (WL.Client.Push){
isSupported = WL.Client.Push.isPushSupported();
}
WL.SimpleDialog.show("Tag Notifications", JSON.stringify(isSupported), [
{
text : 'Close',
handler : function() {
}
},{
text : 'OK',
handler : function(){
if (WL.Client.Push) {
WL.Client.Push.onReadyToSubscribe = function() {
WL.SimpleDialog.show("Tag Notifications", "Ready to subscribe", [
{
text : 'Close',
handler : function() {}
},{
text : 'ok',
handler : function() {
subscribeToSampleTag1();
subscribeToSampleTag2();
isSubscribedToTags();
}
}
]);
$('.subscribeToTagButton').removeAttr('disabled');
$('.unsubscribeFromTagButton').removeAttr('disabled');
};
}
}
}
]);
}
function subscribeToSampleTag1() {
WL.Client.Push.subscribeTag("tag1", {
onSuccess: subscribeTagSuccess,
onFailure: subscribeTagFailure
});
}
function subscribeToSampleTag2() {
WL.Client.Push.subscribeTag("tag2", {
onSuccess: subscribeTagSuccess,
onFailure: subscribeTagFailure
});
}
function isSubscribedToTags() {
var subscribedTagsSample1 = WL.Client.Push.isTagSubscribed("tag1");
var subscribedTagsSample2 = WL.Client.Push.isTagSubscribed("tag2");
WL.SimpleDialog.show("Tag Notifications", 'sample-tag1: ' + subscribedTagsSample1 + '\n' + 'sample-tag2: ' + subscribedTagsSample2, [ {
text : 'Close',
handler : function() {}
}
]);
}
WL.Client.Push.onMessage = function (props, payload) {
WL.SimpleDialog.show("Tag Notifications", "Provider notification data: " + JSON.stringify(props), [ {
text : 'Close',
handler : function() {
WL.SimpleDialog.show("Tag Notifications", "Application notification data: " + JSON.stringify(payload), [ {
text : 'Close',
handler : function() {}
}]);
}
}]);
答案 5 :(得分:0)
#include <iostream>
#include <vector>
#include <string>
#include <cstdlib>
#include <map>
using namespace std;
int main()
{
// freopen("22.txt", "r", stdin);
vector<char> count(10);
map<int, char> Min_1;
Min_1[0] = '8';
Min_1[2] = '1';
Min_1[3] = '7';
Min_1[4] = '4';
Min_1[5] = '2';
count[0] = '6';
count[1] = '2';
count[2] = '5';
count[3] = '5';
count[4] = '4';
count[5] = '5';
count[6] = '6';
count[7] = '3';
count[8] = '7';
count[9] = '6';
int N = 99, P = 2;
cin >> N;
while(N --)
{
cin >> P;
vector<char> min, max;
int a = P, b = P;
int total = (a + 6) / 7;
int left = a % 7;
bool first = true;
char lastInsert = 0;
while(a != 0)
{
if(total == 1)
{
if(left != 6)
{
min.push_back(Min_1[left]);
}else if(left == 6)
{
if(!first)
min.push_back('0');
else
min.push_back('6');
}
break;
}
if(left == 0)
{
min.insert(min.end(), total, '8');
break;
}else{
if(first && left <= 2)
{
min.push_back('1');
lastInsert = 1;
}else if(first && left < 6)
{
min.push_back('2');
lastInsert = 2;
}else if(first && left == 6)
{
min.push_back('6');
lastInsert = 6;
}else if(!first)
{
min.push_back('0');
lastInsert = 0;
}
}
int temp = count[lastInsert] - '0';
a -= temp;
left = a % 7;
total = (a + 6) / 7;
first = false;
}
if(b % 2 == 1)
{
max.push_back('7');
max.insert(max.end(), (b - 3) / 2, '1');
}else{
max.insert(max.end(), b / 2, '1');
}
string res_min = string(min.begin(), min.end());
string res_max = string(max.begin(), max.end());
cout << res_min << " " << res_max << endl;
P ++;
}
return 0;
}
这是我的答案,希望有所帮助