时间限制:2秒/堆栈限制:256MB /内存限制:256MB
问题 戴夫的计算器坏了。当出现超过K个不同的数字时,它会停止。
Dave想要输入一个整数A,但如果他正确输入这个数字,计算器可能会停止。相反,他输入的最接近的整数不会停止计算器。
输出Dave输入和整数A之间的差异。
输入
输入将以标准输入的以下格式给出。
A K
输出
在一行中输出Dave输入和整数A之间的最小差值。确保在输出的末尾插入换行符。
输入示例1
1234 2
输出示例1
12
在这种情况下,Dave最多只能使用2个不同的数字。他将输入最接近的整数1222,因此差值为12。
输入示例2
7328495 10
输出示例2
0
在这种情况下,戴夫的计算器根本没有破坏。他可以按原样输入给定的整数A.
输入示例3
800000 1
输出示例3
22223
Dave只能使用一个数字,因此777777是最接近的整数。
输入示例4
262004 2
输出示例4
218
最接近的整数是262222。
我通过强力解决方案解决了这个问题......
我尝试了所有可能的情况,其中我在1< = i< A或A<我< = 10 ^ 15。
我将得到两个由K个不同数字组成的解决方案,并找出A和解决方案之间的最小差异。
这是一个简单的解决方案。
当A较大时,执行将超过2秒的阈值。
有没有一种聪明有效的方法来解决它?
答案 0 :(得分:0)
第一部分是:选择结果中使用的k个数字(10个中可能的数字)
使用位数学(二项式系数)很容易计算数字
对于给定的k,可能有不同的数字组合
有多达252种可供选择的可能性(如果k = 5,则为252,否则不会这么多)。
如果不清楚我在说什么:对于k = 2,有01,02,03,...09,12,13,...,19,23,24,...,89
不,例如。 11因为它只有1但k = 2允许两个不同的数字
没有例如。 90因为它与09,91 = 19等相同
对于k = 5,这些可能组合的计数为252,并且它不会变大。
查找当前k的所有(最多)252种可能性 并在循环中迭代它们不应该太难。
对于每种的可能性,请执行以下操作:
{
在循环中,我们有A,K和当前选择的有效数字
对于解决方案(当然,后者在循环的每次迭代中都不同)
让我们以A = 12899,K = 3,数字123为例
现在搜索一个(从左到右)的第一个数字,这是不允许的
在该示例中,这将是12 8 99,因为允许其左边1和2。
如果有一个,则将此(8)替换为下一个允许的下一个数字(2)
并用尽可能高的数字替换它的所有内容(3也是如此)
12899 =&GT; 12 <强> 3 强> 99 =&GT 123的 33 强>
将结果12333保存在数组中的某处等。
如果没有下一个可能的下一个数字,则什么也不做(没有任何内容去数组)
(带有进位的正确添加会引入更改后的新数字,左 即。无效。将找到具有该新数字的解决方案 当循环达到那个数字时)
然后在另一个方向再次出现同样的事情:
将找到的数字替换为下一个更高的数字
一切都是最低的。这也在阵列中,
当然只有当下一个更高的数字时才会出现
在12899的情况下,8的唯一更高的替代品是9,
但是9是不可能的(只有123)。
}
整个循环结束后(每次迭代都产生了)
最多两个新阵列条目),您有多达502种可能的解决方案。
剩下要做的唯一事情就是检查哪个溶液是最好的(即计算差异)
到每个条目的A并采取最小差异的解决方案
答案 1 :(得分:0)
在每一步,你可以使用你需要的数字,数字+ 1(模10),数字-1(模10),或你以前用过的任何数字。一旦用完数字,您只能使用之前使用过的数字。
这会创建一棵树。在每一步中,取当前状态并计算下一级中所有可能的新状态。
您可以通过删除任何后续步骤来修剪树,这些步骤的增量距离任何后续步骤的最小增量超过一定距离。
这实际上是线性编程问题。
以下是完整的算法!也许有人可以微调决定修剪哪些分支以使其更快。
using System.IO;
using System;
using System.Collections.Generic;
using System.Linq;
public static class Converter
{
public static int ToInt(this IEnumerable<int> values)
{
return values.Aggregate(0, (total, v) => total * 10 + v);
}
public static IEnumerable<int> ToList (this int value)
{
if (value == 0) return Enumerable.Empty<int>();
else return value.ToString().Select(x => (int)x - '0').ToList();
}
}
class Program
{
static void Main(string[] args)
{
//int desired = 262004;
//int digits = 2;
int desired = 800000;
int digits = 1;
IEnumerable<State> currentStates = new[] { new State(0, 0, desired, digits) };
foreach (var digit in desired.ToList())
{
var nextStates = currentStates.SelectMany(x => x.NextPossibleStates());
foreach (var state in nextStates.OrderBy(s => s.Delta()).Take(10))
{
Console.WriteLine(state.ToString());
}
currentStates = nextStates;
Console.WriteLine("------------");
}
Console.ReadKey();
}
public class State
{
int index;
int current;
int desired;
int digitsRemaining;
private int desiredToNow()
{
return desired.ToList().Take(index).ToInt();
}
public int Delta()
{
return Math.Abs(desiredToNow() - current);
}
public State(int index, int current, int desired, int digitsRemaining)
{
this.index = index;
this.current = current;
this.desired = desired;
this.digitsRemaining = digitsRemaining;
}
private State Next (int nextDigit, int digitsRemaining)
{
return new State(this.index + 1, this.current * 10 + nextDigit, this.desired, digitsRemaining);
}
private IEnumerable<int> NextPossibleDigitsWithDuplicates()
{
if (this.digitsRemaining > 0)
{
int nextDesiredDigit = desired.ToList().Skip(index).First();
yield return nextDesiredDigit;
yield return (nextDesiredDigit + 9) % 10;
yield return (nextDesiredDigit + 1) % 10;
}
// Any previously used digit is OK
foreach (int i in this.current.ToList())
yield return i;
}
public IEnumerable<State> NextPossibleStates()
{
var possibles = this.NextPossibleDigitsWithDuplicates().Distinct();
var possiblesUsingExistingDigits = possibles.Where(p => this.current.ToList().Contains(p));
var possiblesUsingNewDigits = possibles.Where(p => !this.current.ToList().Contains(p));
var states = possiblesUsingExistingDigits.Select(p => Next(p, this.digitsRemaining))
.Concat(possiblesUsingNewDigits.Select(p => Next(p, this.digitsRemaining - 1)))
.ToList();
var bestDelta = states.Min(s => s.Delta());
// Now reject any that can never be better
// Guessing on the '2' here ...
var validStates = states.Where(s => s.Delta() < bestDelta + 2);
return validStates;
}
public override string ToString()
{
return this.current + " d=" + this.Delta() + " remaining " + this.digitsRemaining;
}
}
}
答案 2 :(得分:0)
在考虑了一下这个问题后,我猜一个从左到右分配数字的A *算法是最好的。为此,我们需要估计部分解的误差的下限。一个简单的可能性是:
采用示例问题12899 3
算法将如下工作:
开始节点
node best error
xxxxx 12899 0
扩展最有希望的部分解决方案xxxxx
node best error
0xxxx 09999 2900
1xxxx 12899 0
2xxxx 20000 7101
3xxxx 30000 17101
4xxxx 40000 27101
5xxxx 50000 37101
6xxxx 60000 47101
7xxxx 70000 57101
8xxxx 80000 67101
9xxxx 90000 77101
扩展最有希望的部分解决方案1xxxx
node best error
0xxxx 09999 2900
10xxx 10999 1900
11xxx 11999 900
12xxx 12899 0
13xxx 13000 101
14xxx 14000 1101
15xxx 15000 2101
16xxx 16000 3101
17xxx 17000 4101
18xxx 18000 5101
19xxx 19000 6101
2xxxx 20000 7101
3xxxx 30000 17101
4xxxx 40000 27101
5xxxx 50000 37101
6xxxx 60000 47101
7xxxx 70000 57101
8xxxx 80000 67101
9xxxx 90000 77101
扩展最有希望的部分解决方案12xxx
node best error
0xxxx 09999 2900
10xxx 10999 1900
11xxx 11999 900
120xx 12099 800
121xx 12199 700
122xx 12299 600
123xx 12399 500
124xx 12499 400
125xx 12599 300
126xx 12699 200
127xx 12799 100
128xx 12899 0
129xx 12900 1
13xxx 13000 101
14xxx 14000 1101
15xxx 15000 2101
16xxx 16000 3101
17xxx 17000 4101
18xxx 18000 5101
19xxx 19000 6101
2xxxx 20000 7101
3xxxx 30000 17101
4xxxx 40000 27101
5xxxx 50000 37101
6xxxx 60000 47101
7xxxx 70000 57101
8xxxx 80000 67101
9xxxx 90000 77101
扩展最有希望的部分解决方案128xx(现在只允许数字1,2和8)
node best error
0xxxx 09999 2900
10xxx 10999 1900
11xxx 11999 900
120xx 12099 800
121xx 12199 700
122xx 12299 600
123xx 12399 500
124xx 12499 400
125xx 12599 300
126xx 12699 200
127xx 12799 100
1281x 12819 80
1282x 12829 70
1288x 12889 10
129xx 12900 1
13xxx 13000 101
14xxx 14000 1101
15xxx 15000 2101
16xxx 16000 3101
17xxx 17000 4101
18xxx 18000 5101
19xxx 19000 6101
2xxxx 20000 7101
3xxxx 30000 17101
4xxxx 40000 27101
5xxxx 50000 37101
6xxxx 60000 47101
7xxxx 70000 57101
8xxxx 80000 67101
9xxxx 90000 77101
扩展最有希望的部分解决方案129xx(现在只允许数字1,2和9)
node best error
0xxxx 09999 2900
10xxx 10999 1900
11xxx 11999 900
120xx 12099 800
121xx 12199 700
122xx 12299 600
123xx 12399 500
124xx 12499 400
125xx 12599 300
126xx 12699 200
127xx 12799 100
1281x 12819 80
1282x 12829 70
1288x 12889 10
1291x 12910 11
1292x 12920 21
1299x 12990 91
13xxx 13000 101
14xxx 14000 1101
15xxx 15000 2101
16xxx 16000 3101
17xxx 17000 4101
18xxx 18000 5101
19xxx 19000 6101
2xxxx 20000 7101
3xxxx 30000 17101
4xxxx 40000 27101
5xxxx 50000 37101
6xxxx 60000 47101
7xxxx 70000 57101
8xxxx 80000 67101
9xxxx 90000 77101
扩展最有希望的部分解决方案1288x(现在只允许数字1,2和8)
node best error
0xxxx 09999 2900
10xxx 10999 1900
11xxx 11999 900
120xx 12099 800
121xx 12199 700
122xx 12299 600
123xx 12399 500
124xx 12499 400
125xx 12599 300
126xx 12699 200
127xx 12799 100
1281x 12819 80
1282x 12829 70
12881 12881 18
12882 12882 17
12888 12888 11 <-- optimal solution found
1291x 12910 11
1292x 12920 21
1299x 12990 91
13xxx 13000 101
14xxx 14000 1101
15xxx 15000 2101
16xxx 16000 3101
17xxx 17000 4101
18xxx 18000 5101
19xxx 19000 6101
2xxxx 20000 7101
3xxxx 30000 17101
4xxxx 40000 27101
5xxxx 50000 37101
6xxxx 60000 47101
7xxxx 70000 57101
8xxxx 80000 67101
9xxxx 90000 77101
这仍然不包括最佳解决方案可能比原始数字更多位数的情况(不确定这是否可能),但您明白了......
答案 3 :(得分:0)
对于K = 10,解决方案是微不足道的。差异等于0。
如果原始数字使用小于或等于K不同数字。差值等于0.例如,987777 5
使用3个不同的数字,小于K(= 5)。
对于K = 1,
长度为n的数字将被999 ... 999(n位)和999 ... 999((n-1)-digit)约束。这意味着587 is bound by 999 and 99
。因此,解决方案必须是n位数长度或999 ... 999((n-1)-digit)。
让数字的前导数字为a
(587中为5),表示b=a+1
和c=a-1
。将该数字与aaa...aaa
,bbb...bbb
,ccc...ccc
(所有n位长度)进行比较。请注意,这只是检查其中最多2个。
original number = aaa...aaa
,则差值等于0. original number > aaa...aaa
,请继续与bbb...bbb
进行比较。original number < aaa...aaa
,请继续与ccc...ccc
进行比较。 K = 1的规则,如果b=10
,则忽略bbb...bbb
,因为aaa...aaa = 999...999
是上限。如果c=0
,请将ccc...ccc
更改为999...999
((n-1)-digit)以进行下限检查,例如10000 1 -> 9999
对于范围[2,9]中的K,请观察first K-1 distinct digit from left to right must be used
。例如,在这种情况下,必须使用9988898988981234 3
, 2 数字9和8。
<强>证明:强>
事实:
我们总是可以使用K个不同的数字形成两个值z000...000
和z999...999
,其中z
由K-1个不同的数字(上例中的z = 998889898898
)组成并分配第K个数字为0或9(忽略K-1
中可能使用0或9的事实并不重要)
使用z000...000
和z999...999
(9988898988980000
和9988898988989999
),最佳解决方案受两个值的约束。如果z变为z + 1或z-1,则差异将增加,这是没有意义的。因此,将始终使用z部分并且first K-1 distinct digit from left to right must be used
成立。
证明结束
在第一次遇到第K个不同数字(z部分后面的前导数字,表示为t
)时,有三种可能需要考虑的案例:t
,t+1
和{{ 1}}。
让我们先从t-1
开始。使用t
将耗尽所有K数字,因此不能再分配数字。继续检查右边部分的数字,直到我们遇到一个不在那个K不同数字的数字。这次,我们最多有两个选择。 K中最接近的数字大于遇到数字,K中最接近的数字小于遭遇数字。在K中使用较大的最接近数字将导致数字大于原始数字,因此应通过选择K中的最小数字来最小化剩余数字。类似地,K情况下较小最接近数字的剩余数字将是K中的最大数字。 / p>
对于案例t
,只有在案例t+1
的K中没有更大的最接近数字时才需要检查。使用t
将始终大于原始数字,我们需要最小化剩余数字。请注意,t+1
可能是K-1中的一个数字,我们有一个免费数字,因此请加t+1
(如果0
不在K-1中)。然后,我们可以选择K中的最小数字(或0
时为K-1,t+1
中为0
)。
对于案例K-1
,只有在案例t-1
的K中没有较小的最接近数字时才需要检查。使用t
将始终小于原始数字,我们需要最大化剩余数字。请注意,t-1
可能是K-1中的一个数字,我们有一个免费数字,因此请加t-1
(如果9
不在K-1中)。然后,我们可以选择K中的最大数字(如果9
为{K-1,t-1
为9
),则为剩余数字。
答案 4 :(得分:0)
解决时间为O(len(A))。 找到最小| A - B |,具有不同的K位数。 首先,为了去除abs,我们应该找到具有条件的B1(min(Bi | Bi> = A,具有不同的数字编号),
设A = a1a2 ... an,B1 = b1b2 ... bn,找到ai = bi的最长序列,直到不同的数量超过K.假设我们有a1-> aj = b1-> BJ
如果j == n,则B1 = A
从{a1,a2,...,aj}的集合中,找到大于b [j + 1]的最小数字
如果找到ak(1 <= k <= j),那么令b [j + 1] = ak,其余的b [j + 1] - > bn用min {a1,a2, ...,AJ}
如果没有找到,我们将bj从aj更改为[j] +1。对于b [j + 1]的其余部分 - > bn,
如果[j] +1在{a1,a2,...,aj-1}的集合中,则意味着我们仍然可以按下一个数字,我们选择0作为填充。 b [j + 1] - &gt; bn全部用0填充。
如果[j] +1不在{a1,a2,...,aj-1}的集合中,我们选择min {a1,a2,...,aj-1,a [j] +1}作为用b [j + 1] - >&gt; bn
填充的值相似性,我们可以找到最接近的数字B2,它小于A,
之后,我们只是将B1-A与A-B2进行比较,选择最小值。
遵循C ++代码:
#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <sstream>
using namespace std;
string target_num;
string up_num;
string down_num;
int K;
bool hash_key[10];
bool hash_copy[10];
int find_closet_k(int c, bool up) {
int i = c;
while(true) {
if (hash_key[i]) {
return i;
}
if(up) {
++i;
if (10 <= i) {
break;
}
} else {
--i;
if (i < 0) {
break;
}
}
}
return -1;
}
char min_k() {
for (int i = 0; i < 10; ++i) {
if (hash_key[i]) {
return '0' + i;
}
}
}
char max_k() {
for (int i = 9; 0 <= i; --i) {
if (hash_key[i]) {
return '0' + i;
}
}
}
bool is_head_zero(int v, string num,int end_pos) {
if (0 == v) {
for (int i = 0; i < end_pos; ++i) {
if (num[i] != '0') {
return false;
}
}
return true;
}
return false;
}
int string2int(string s) {
std::stringstream ss;
ss << s;
int is;
ss >> is;
return is;
}
void find_closest() {
int k = 0;
int i = 0;
for (i = 0; i < target_num.size(); ++i) {
int c = target_num[i] - '0';
if (!hash_key[c]) {
if (k == K) {
break;
}
hash_key[c] = true;
++k;
}
up_num.push_back(target_num[i]);
down_num.push_back(target_num[i]);
}
if (i == target_num.size()) {
printf("0\n");
return;
}
copy(hash_key, hash_key + 10, hash_copy);
int j = i - 1;
up_num.resize(target_num.size());
int c = target_num[j + 1] - '0';
int v = find_closet_k(c, true);
if (v == -1) {
int aj = target_num[j] - '0';
up_num[j] = aj + 1 + '0';
if (hash_key[aj + 1]) {
//only used K - 1, use 0 as min_k();
fill(up_num.begin() + j + 1, up_num.end(), '0');
} else {
hash_key[aj] = false;
hash_key[aj + 1] = true;
fill(up_num.begin() + j + 1, up_num.end(), min_k());
}
} else {
up_num[j + 1] = v + '0';
fill(up_num.begin() + j + 1, up_num.end(), min_k());
}
copy(hash_copy, hash_copy + 10, hash_key);
j = i - 1;
down_num.resize(target_num.size());
v = find_closet_k(c, false);
if (v == -1) {
int aj = target_num[j] - '0';
down_num[j] = aj - 1 + '0';
if (hash_key[aj - 1] || is_head_zero(aj - 1,down_num, j)) {
fill(down_num.begin() + j + 1, down_num.end(), '9');
} else {
hash_key[aj] = false;
hash_key[aj -1] = true;
fill(down_num.begin() + j + 1, down_num.end(), max_k());
}
} else {
down_num[j + 1] = v + '0';
fill(down_num.begin() + j + 1, down_num.end(), max_k());
}
unsigned __int64 upi;
unsigned __int64 targeti;
unsigned __int64 downi;
sscanf(up_num.c_str(), "%lld", &upi);
sscanf(target_num.c_str(),"%lld",&targeti);
sscanf(down_num.c_str(),"%lld",&downi);
unsigned __int64 delta = min(upi - targeti, targeti - downi);
printf("%lld\n", delta);
}
int main(){
while (true) {
cin>>target_num>>K;
fill(hash_key, hash_key + 10, false);
find_closest();
up_num.clear();
down_num.clear();
target_num.clear();
}
}
答案 5 :(得分:-1)
编辑N2
每一步检查是否:
a)将密集的可能数字减去然后当前,然后最大化剩余数字优于当前回答
b)将最接近的可能数字更大然后当前,然后最小化剩余数字优于当前答案
然后如果可能的话加上相同的数字,然后继续下一个数字