POJ3717 Decrypt the Dragon Scroll
Description
Those who have see the film of "Kong Fu Panda" must be impressive when Po opens the dragon scroll, because nothing was recorded on it! Po was surprising at the situation; Tai Lung and Master Shifu were also surprising since no body believes that the mystic dragon scroll is just a blank paper. After Tai Lung was defeated, Po found Master Wugui’s Diary and know that the dragon scroll recorded some messages long long years ago, but these messages were blurred due to abrasions. Master Wugui has copied these blurred messages and he wants someone to recover these messages. The messages has a specific length, and each position could be digit, '?' or ','. It is known that each message recorded some strictly increasing positive integers (without leading zeroes) separated by commas and Po is asked to recover these numbers.
Input
There are multiple test cases. Each test case contains string in a line represented. The length of the string will not exceed \(500\). The format is shown in the sample input.
Output
If the message has no appropriate solution, print "impossible", else print the decrypted message. If there exists multiple solutions, output the one whose first number is the smallest; if there is a tie, output the one whose second number is the smallest; and so on.
Sample Input
?,10,?????????????????,16,??
?2?5??7?,??
???????????????????????????????,???
Sample Output
impossible
12,50,70,71
1,2,3,4,5,6,7,8,9,10,11,100,101,102
这个题目dp应该很好想,然后就是难得写。
我们用\(f_i\)表示从\(i\)这个位置开始的合法序列第一个数字最大是多少。然后很容易想到dp来求\(f_i\)。
这个\(maxconvert\)的意思是在\([i,j]\)中填数字,\(j+1\)上填',',构造出比\(f_{j+2}\)小最大的数字是多少。
然后这个\(maxconvert\)写起来有些日狗。
我们将\(S_i \sim S_j\)记作\(S\),将\(f_{j+2}\)记作\(pat\)。
我们抓住这一点——我们肯定是要找到最后大的\(id\),使得\(S_1 \sim S_{id-1} = pat_1 \sim pat_{id-1}\),然后\(S_{id} < pat_{id}\),且\(S_{id}\)要填满足的最大值。\(S_{id+1} \sim\)的'?'全部换成'9'。
之后有了\(f\),我们就可以判断有无解了。
但是还要输方案,我们可以采用贪心的思想,每次都填最小的。假设我们已经填好了\(1 \sim i\),要在\(i+2 \sim j\)中填上数字,我们只要保证填的数字小于\(f_{j+2}\)即可。我们可以构造一个\(minconvert(i,j,j+2)\)函数,表示在\([i,j]\)中填数字,使得填出来的数字比\(S_1 \sim S_j-1\)的数字中的最大值大的最小值是多少。当然要保证此数字小于\(f_{j+2}\)。然后这题就做完了。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
const int maxn = 510;
char S[maxn]; int N;
struct Node
{
char s[maxn]; int len;
friend inline bool operator <(const Node &a,const Node &b)
{
if (a.len != b.len) return a.len < b.len;
for (int i = 0;i < a.len;++i) if (a.s[i] != b.s[i]) return a.s[i] < b.s[i];
return 0;
}
inline Node &operator=(const Node &a)
{
if (this == &a) return *this;
len = a.len;
for (int i = 0;i < len;++i) s[i] = a.s[i]; s[len] = 0;
return *this;
}
inline Node():len(0) { s[len] = 0; }
}f[maxn],now;
inline void maxconvert(int x,int y,const Node &z)
{
if (y+2 < N&&y-x+1 > z.len) return;//位数多了,gg
Node tmp;
if (y+2 >= N||y-x+1 < z.len)
{
for (int i = x;i <= y;++i) tmp.s[tmp.len++] = (S[i] == '?'?'9':S[i]);
if (tmp.s[0] != '0') f[x] = tmp;
return;
}//位数少了,补9
int id = -1; //位数一样
for (int i = x;i <= y;++i)
{
if (S[i] == z.s[i-x]) continue;
if (S[i] != '?'&&S[i] > z.s[i-x]) { if (id != -1) break; return; }
if (S[i] == '?'&&z.s[i-x] == '0') continue;
if (S[i] == '?'&&z.s[i-x] == '1'&&i == x) continue;
id = i;
if (S[i] != '?'&&S[i] < z.s[i-x]) break;
}
if (id == -1) return;
for (int i = x;i < id;++i) tmp.s[tmp.len++] = z.s[i-x];
if (S[id] == '?') tmp.s[tmp.len++] = z.s[id-x]-1;
else tmp.s[tmp.len++] = S[id];
for (int i = id+1;i <= y;++i) tmp.s[tmp.len++] = (S[i] == '?'?'9':S[i]);
if (tmp.s[0] != '0') f[x] = tmp;
}
inline bool minconvert(int x,int y,const Node &z)
{
if (y-x+1 < now.len) return false;//位数多了,gg
Node tmp;
if (y-x+1 > now.len)
{
tmp.s[tmp.len++] = (S[x] == '?'?'1':S[x]);
for (int i = x+1;i <= y;++i) tmp.s[tmp.len++] = (S[i] == '?'?'0':S[i]);
if (tmp.s[0] != '0'&&(y+1 >= N||tmp < z))
{
for (int i = x;i <= y;++i) S[i] = tmp.s[i-x];
now = tmp; return true;
}
return false;
}
int id = -1;
for (int i = x;i <= y;++i)
{
if (now.s[i-x] == S[i]) continue;
if (S[i] != '?'&&S[i] < now.s[i-x]) { if (id != -1) break; return false; }
if (S[i] == '?'&&now.s[i-x] == '9') continue;
id = i;
if (S[i] != '?'&&S[i] > now.s[i-x]) break;
}
if (id == -1) return false;
for (int i = x;i < id;++i) tmp.s[tmp.len++] = now.s[i-x];
if (S[id] == '?') tmp.s[tmp.len++] = now.s[id-x]+1;
else tmp.s[tmp.len++] = S[id];
for (int i = id+1;i <= y;++i) tmp.s[tmp.len++] = (S[i] == '?'?'0':S[i]);
if (tmp.s[0] != '0'&&(y+1 >= N||tmp < z))
{
for (int i = x;i <= y;++i) S[i] = tmp.s[i-x];
now = tmp; return true;
}
return false;
}
int main()
{
freopen("3717.in","r",stdin);
freopen("3717.out","w",stdout);
while (scanf("%s",S) != EOF)
{
N = strlen(S);
for (int i = 0;i < N+10;++i) f[i].len = 0;
for (int i = N-1;i >= 0;--i)
{
if (S[i] == ',') continue;
for (int j = i;j < N;++j)
{
if (j+1 >= N||S[j+1] == ','||(S[j+1] == '?'&&j+2 < N&&S[j+2] != ','))
{
maxconvert(i,j,f[j+2]);
if (S[j+1] == ',') break;
}
}
}
now.len = 0; bool fnd = true;
for (int i = 0;i < N;++i)
{
if (S[i] == ',') continue;
bool succ = false;
for (int j = i;j < N;++j)
if (j+1 >= N||S[j+1] == ','||(S[j+1] == '?'&&j+2 < N&&S[j+2] != ','))
{
if (minconvert(i,j,f[j+2]))
{
if (S[j+1] == '?') S[j+1] = ',';
succ = true; i = j; break;
}
if (S[j+1] == ',') break;
}
if (!succ) { fnd = false; break; }
}
if (!fnd) puts("impossible");
else puts(S);
}
fclose(stdin); fclose(stdout);
return 0;
}