题目
http://community.topcoder.com/stat?c=problem_statement&pm=13964&rd=16514&rm=326780&cr=22918502
题解:
http://apps.topcoder.com/wiki/display/tc/SRM+665
Div2 Hard / Div1 Easy: LuckySum
Consider the straight-forward bruteforce approach: For each of the n digit positions in the two lucky numbers we pick 4 or 7, then we try if the resulting sum matches the provided pattern and return the smallest valid one. There are 2n slots each with 2 options, so we will be trying 22n=4n combinations. For n=14, this will be too large.
474474
447447
+ --------
921921
There is a cool observation we can make. When in a digit position i, the two lucky digits are distinct (4 + 7 or 7 + 4), it doesn't matter what order we assign to them. The sum up there has the same result as the following:
444444
477477
+ --------
921921
Since we are only interested in the final sum and not the specific lucky numbers used to find it, then we can just always make sure to use 4+7 and ignore the cases with 7+4. This means that for each digit position we will have 3 options instead of four: 4+4, 4+7, 7+7. This changes the complexity, to 3n down from 4n. 3n is small enough.
That finishes the theoretic part of how to solve the problem. In practice, however, there is one more complications we'll meet: The two lucky numbers won't necessarily have exactly n digits. Here are some variations:
4
7
+ ----
11
444
7
+ ------
451
This is the same as if we added leading zeros to the numbers. So now we have to pick 4, 7 or 0 for each digit slot:
04
07
+ ----
11
444
007
+ ------
451
Does this mean that we have a larger complexity than 3n ? In reality it won't, because once we add a zero, all the digits we pick to the left of that zero digit must be 0 as well. The other lucky number might still have two options: 4 or 7. So imagine this: We initially have n digit positions, three of the options make it so there are O(3n−1) remaining options, two other options will make it so there are O(2n−1) remaining options so we have something like O(3⋅3n−1+2⋅2n−1)≡O(3n)
We also need to be careful in implementation. Some examples: If we choose to do the addition as we pick the digits, we need to simulate things like the carry. Make sure 0 is not considered a lucky number. The last digit position might have two 0s but only if there is carry. Etc. The following solution uses backtracking:
附上tourist代码
题解:
http://apps.topcoder.com/wiki/display/tc/SRM+665
Div2 Hard / Div1 Easy: LuckySum
Consider the straight-forward bruteforce approach: For each of the n digit positions in the two lucky numbers we pick 4 or 7, then we try if the resulting sum matches the provided pattern and return the smallest valid one. There are 2n slots each with 2 options, so we will be trying 22n=4n combinations. For n=14, this will be too large.
474474
447447
+ --------
921921
There is a cool observation we can make. When in a digit position i, the two lucky digits are distinct (4 + 7 or 7 + 4), it doesn't matter what order we assign to them. The sum up there has the same result as the following:
444444
477477
+ --------
921921
Since we are only interested in the final sum and not the specific lucky numbers used to find it, then we can just always make sure to use 4+7 and ignore the cases with 7+4. This means that for each digit position we will have 3 options instead of four: 4+4, 4+7, 7+7. This changes the complexity, to 3n down from 4n. 3n is small enough.
That finishes the theoretic part of how to solve the problem. In practice, however, there is one more complications we'll meet: The two lucky numbers won't necessarily have exactly n digits. Here are some variations:
4
7
+ ----
11
444
7
+ ------
451
This is the same as if we added leading zeros to the numbers. So now we have to pick 4, 7 or 0 for each digit slot:
04
07
+ ----
11
444
007
+ ------
451
Does this mean that we have a larger complexity than 3n ? In reality it won't, because once we add a zero, all the digits we pick to the left of that zero digit must be 0 as well. The other lucky number might still have two options: 4 or 7. So imagine this: We initially have n digit positions, three of the options make it so there are O(3n−1) remaining options, two other options will make it so there are O(2n−1) remaining options so we have something like O(3⋅3n−1+2⋅2n−1)≡O(3n)
We also need to be careful in implementation. Some examples: If we choose to do the addition as we pick the digits, we need to simulate things like the carry. Make sure 0 is not considered a lucky number. The last digit position might have two 0s but only if there is carry. Etc. The following solution uses backtracking:
const long INF = 12345678901234567LL;
long result;
vector<int> note, A, B, C;
// p :current digit position
// x: current value of the number we are building
// ten_p: 10^p
void backtrack(int p, long x, long ten_p)
{
if (p == note.size()) {
if (x < result) {
result = x;
}
} else {
for (int j : {0,4,7} ) { //Pick B[p]
// If B[p-1] was 0, we cannot use a non-zero one anymore
if ( (j != 0) && (p > 0) && (B[p-1] == 0) ) {
continue;
}
// If p == 0, B[p] shouldn't be zero, because '0' is not a magic number
if ( (j == 0) && (p == 0) ) {
continue;
}
for (int i : {7,4,0} ) { //Pick values for A[p]
// Ignoring cases where i < j , optimizes the search to O(3^n)
if (i < j) {
continue;
}
A[p] = i;
B[p] = j;
// Current carry (depends on p-1 )
int c = ( ( (p!=0) && (A[p-1] + B[p-1] >= 10) ) ? 1 : 0 );
if ( (i == 0) && (j == 0) && ( (p != C.size() -1) || (c != 1) ) ) {
// A case where both digits are zero is valid only when
// the previous step had carry and this is the last step.
continue;
}
C[p] = (c + i + j) % 10;
// if there's carry this shouldn't be the last step
if ( (p == C.size() - 1) && (c + i + j >= 10) ) {
continue;
}
// things should match with the note
if ( (note[p] != -1) && (note[p] != C[p]) ) {
continue;
}
// move on to the next step
backtrack(p+1, x + C[p] * ten_p, ten_p * 10);
}
}
}
}
long construct(string note)
{
int n = note.size();
this->note.resize(n);
A.resize(n);
B.resize(n);
C.resize(n);
for (int i = 0; i < n; i++) {
this->note[n-i-1] = ( (note[i] == '?') ? -1 : (note[i] - '0') );
}
result = INF;
backtrack(0, 0LL, 1);
return (result >= INF) ? -1: result;
}
附上tourist代码
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
using namespace std;
class LuckySum {
public:
long long construct(string);
};
int len, a[4242];
long long ans;
void dfs(int pos, int alive, int carry, long long num, long long step);
void test_add(int pos, int alive, int carry, long long num, long long step, int add) {
int digit = add + carry;
int new_carry = 0;
if (digit >= 10) {
digit -= 10;
new_carry = 1;
}
if (pos == 0 && digit == 0) {
return;
}
if (a[pos] != -1 && a[pos] != digit) {
return;
}
dfs(pos - 1, alive, new_carry, num + step * digit, step * 10);
}
void dfs(int pos, int alive, int carry, long long num, long long step) {
if (pos == -1) {
if (carry == 1) {
return;
}
if (ans == -1 || num < ans) {
ans = num;
}
return;
}
if (alive == 0) {
test_add(pos, alive, carry, num, step, 0);
return;
}
if (pos != len - 1) {
dfs(pos, alive - 1, carry, num, step);
}
if (alive == 1) {
test_add(pos, alive, carry, num, step, 4);
test_add(pos, alive, carry, num, step, 7);
} else {
test_add(pos, alive, carry, num, step, 8);
test_add(pos, alive, carry, num, step, 11);
test_add(pos, alive, carry, num, step, 14);
}
}
long long LuckySum::construct(string note) {
len = note.length();
for (int i = 0; i < len; i++) {
if (note[i] == '?') {
a[i] = -1;
} else {
a[i] = note[i] - '0';
}
}
ans = -1;
dfs(len - 1, 2, 0, 0, 1);
return ans;
}