Bi-peak Number
Problem Description
A peak number is defined as continuous digits {D0, D1 … Dn-1} (D0 > 0 and n >= 3), which exist Dm (0 < m < n - 1) satisfied Di-1 < Di (0 < i <= m) and Di > Di+1 (m <= i < n - 1).
A number is called bi-peak if it is a concatenation of two peak numbers.
The score of a number is the sum of all digits. Love8909 is crazy about bi-peak numbers. Please help him to calculate the MAXIMUM score of the Bi-peak Number in the closed interval [A, B].
A number is called bi-peak if it is a concatenation of two peak numbers.
The score of a number is the sum of all digits. Love8909 is crazy about bi-peak numbers. Please help him to calculate the MAXIMUM score of the Bi-peak Number in the closed interval [A, B].
Input
The first line of the input is an integer T (T <= 1000), which stands for the number of test cases you need to solve.
Each case consists of two integers “A B” (without quotes) (0 <= A <= B < 2^64) in a single line.
Each case consists of two integers “A B” (without quotes) (0 <= A <= B < 2^64) in a single line.
Output
For the kth case, output “Case k: v” in a single line where v is the maximum score. If no bi-peak number exists, output 0.
Sample Input
3 12121 12121 120010 120010 121121 121121
Sample Output
Case 1: 0 Case 2: 0 Case 3: 8
最好先看下windy这道题的解题报告解题报告和代码见code:
/*
*如果你看懂了windy那道题的思路,那么你将更容易懂以下这种思路:
* 首先明确一个无后效性:前缀跟后缀的关系只在于前缀的最后一个数字和后缀的第一个数字的关系,这个关系只影响状态,而不影响其值。
* 这样预处理才是有效的。
* 这次的问题,对于区间[a,b]的求解不能转化为[0,b]的值-[0,a-1]的值,所以我们要同时考虑。
* 以下是处理区间(a,b),对应于闭区间[a+1,b-1].
* 仍然是区间拆分:
* 设b的每一位分别是b0 b1 b2...bn (b0>0)
* 设a的每一位分别是a0 a1 a2...an (此处为了让a与b对齐,不足的补0,即a0可以为0)
* 拆分如下:
* 设a、b前i位相同,第i+1位不同
* [ a0 a1 a2 ... ai-1 1+ai 0 0 0... 0, a0 a1 a2 ... ai-1 bi 0 0 0... 0)
* [ a0 a1 a2 ... ai-1 bi 0 0 0... 0, a0 a1 a2 ... ai-1 bi bi+1 0 0... 0)
* [ a0 a1 a2 ... ai-1 bi bi+1 0 0... 0, a0 a1 a2 ... ai-1 bi bi+1 bi+2 0... 0)
* [ a0 a1 a2 ... ai-1 bi bi+1 bi+2 0... 0, a0 a1 a2 ... ai-1 bi bi+1 bi+2 bi+3... 0)
* ...
* [ a0 a1 a2 ... ai-1 bi bi+1 bi+2 bi+3... 0, a0 a1 a2 ... ai-1 bi bi+1 bi+2 bi+3...bn)
*
* [ a0 a1 a2 ... ai-1 ai 1+ai+1 0 0... 0, a0 a1 a2 ... ai-1 1+ai 0 0 0... 0)
* [ a0 a1 a2 ... ai-1 ai ai+1 1+ai+2 0... 0, a0 a1 a2 ... ai-1 ai 1+ai+1 0 0... 0)
* [ a0 a1 a2 ... ai-1 ai ai+1 ai+2 1+ai+3... 0, a0 a1 a2 ... ai-1 ai ai+1 1+ai+2 0... 0)
* ...
* [ a0 a1 a2 ... ai-1 ai ai+1 ai+2 ai+3...an+1, a0 a1 a2 ... ai-1 ai ai+1 ai+2 ai+3... 0)
* 注意这里在每一位上加1时不进位,只是为了表示一个范围,如果不合法,就是空集,空集不用求。
* 举一个具体例子:(32114539687,32148102939)
* [32120000000,32140000000) 只与dp[7][2..3]有关
* [32140000000,32148000000) 只与dp[6][0..7]有关
* [32148000000,32148100000) 只与dp[5][0..0]有关
* [32148100000,32148100000)*********这个就是空集
* [32148100000,32148102000) 只与dp[3][0..1]有关
* [32148102000,32148102900) 只与dp[2][0..8]有关
* [32148102900,32148102930) 只与dp[1][0..2]有关
* [32148102930,32148102939) 只与dp[0][0..8]有关
*
* [32115000000,32120000000) 只与dp[6][5..9]有关
* [32114600000,32115000000) 只与dp[5][6..9]有关
* [32114540000,32114600000) 只与dp[4][4..9]有关
* [32114539700,32114540000) 只与dp[2][7..9]有关
* [32114539690,32114539700) 只与dp[1][9..9]有关
* [32114539688,32114539690) 只与dp[0][8..9]有关
* dp[i][j]表示i+1位的数字,最高位为j
* 然后就是两座山的不同位置的6个状态,再在dp上加一维。再写一个函数来转移状态。
*
*
*/
#include <cstdio>
#include <cstring>
#include <iostream>
#define N 35
#define OVER 7
typedef unsigned long long ll;
using namespace std;
int ans,len;
char A[N],B[N];
int dp[N][10][OVER+1];
//状态转移函数
int next(int now,int p,int q)
{
switch(now)
{
case 0:return p>0?1:OVER; //山1头 转 山1顶 必须有
case 1:return p>0?1:p<0?2:OVER; //山1顶 转 山1顶/山1尾 必须有
case 2:return p<0?3:q?4:OVER; //山1尾 转 山1缀 必须有
case 3:return p<0?3:p&&q-p?5:q?4:OVER; //山1缀 转 山1缀/山2头/山2顶 不必须,作为尾的后缀,可能作为山2头(当前数字不能为0),故可以直接跳到5
case 4:return p>0?5:OVER; //山2头 转 山2顶 必须有
case 5:return p<0?6:p>0?5:OVER; //山2顶 转 山2顶/山2尾 必须有
case 6:return p<0?6:OVER; //山2尾 转 山2尾 必须有
default:return OVER; //非法状态 OVER为非法状态,前面的所有状态一旦转移失败就返回OVER
}
}
//dp预处理,dp[i][j][k]为i+1位的数字,最高位为j,最高位在山的k位置,最后一位在山的最后位置,所有位上数字和的最大值。
void DP()
{
//为了防止非法状态的转移使得非法状态有解,故使初始值都为一个很大的负数。
memset(dp,128,sizeof(dp));
//处理山的最后位置
for(int i = 0 ; i < 10 ; i++)
dp[0][i][OVER-1] = i;
//往山前处理
for(int i = 1 ; i < N ; i++)
for(int j = 0 ; j < 10 ; j++)
for(int l = 0 ; l < OVER ; l++)
for(int k = 0 ; k < 10 ; k++)
dp[i][j][l] = max(dp[i][j][l] , dp[i-1][k][next(l,k-j,k)]+j);
}
//处理A、B相同前缀
void SolveSamePre(int &i,int &now,int &sum)
{
for(i = 0 ; A[i] && A[i]==B[i] ; i++)
{
now = !i ? 0 : next(now,A[i]-A[i-1],A[i]-'0');
sum += A[i]-'0';
}
}
void SolveB(int st,int now,int sum)
{
for(int i = st; B[i] ; i++)
{
for( int j = i==st? A[i] - '0' + 1 : 0 ; j < B[i] - '0' ; j++)
ans = max(ans,dp[len-i][j][!i ? 0 : next(now,j-B[i-1]+'0',j)]+sum);
//添加前缀,转移状态,更新sum
now = !i ? 0 : next(now,B[i]-B[i-1],B[i]-'0');
sum += B[i]-'0';
}
}
//对于a位数比b位数少的处理,答案很可能在这个范围内诞生
bool SolveLackBit(int &st,int &now,int &sum)
{
if(!st)
{
while(A[st]=='0')
{
st++;
//如果a是0,此时就可能溢出了,最终一直WA就卡在了这里。。。。
if(!A[st])return false;
for(int i = A[st]=='0'?1:A[st]-'0'+1 ; i < 10 ; i++)
ans = max(ans,dp[len-st][i][0]);
}
now = 0;
}else now = next(now,A[st]-A[st-1],A[st]-'0');
sum = sum + A[st] - '0';
return true;
}
//a关联区间的转移
void SolveA(int st,int now,int sum)
{
for(int i = st;A[i]; i++)
{
for( int j = A[i] - '0' + 1 ; j < 10 ; j++)
ans = max(ans,dp[len-i][j][next(now,j-A[i-1]+'0',j)]+sum);
now = next(now,A[i]-A[i-1],A[i]-'0');
sum += A[i] - '0';
}
}
int find(ll a,ll b)
{
//将b转化成字符串放到B里
sprintf(B,"%llu",b);
len = strlen(B);
//将a转化为字符串放到A里,并使得A与B长度一致,不足的补0
sprintf(A,"%0*llu",len--,a);
//SamePreLen 为A、B相同前缀的长度,sum为前缀的和,now为前缀的状态
int SamePreLen = 0, sum = 0, now = 0;
ans = 0;
SolveSamePre(SamePreLen,now,sum);
SolveB(SamePreLen,now,sum);
if(SolveLackBit(SamePreLen,now,sum))
SolveA(SamePreLen+1,now,sum);
return ans;
}
int main()
{
int t,cas = 1;
ll a,b;
scanf("%d",&t);
DP();
while(t--)
{
scanf("%llu %llu",&a,&b);
if(a>b)a^=b^=a^=b;
//a为0和b为最大的特判,防止溢出带来的不可料后果。。。。
if(!a)a++;
if(!(b+1))b--;
printf("Case %d: %d\n",cas++,find(a-1,b+1));
}
return 0;
}
//代码长度100多行,不加define等各种优化来缩减长度,还是比较清晰的。没有拆分成几个函数时,在hdu上跑了0ms,排到了第一!!拆成几个函数就跑了15ms。。。。
Run ID | Submit Time | Judge Status | Pro.ID | Exe.Time | Exe.Memory | Code Len. | Language | Author |
4353197 | 2011-08-07 13:13:03 | Accepted | 3565 | 0MS | 204K | 2536 B | C++ | 578559967 |
未分函数的代码(0ms):
#include <cstdio>
#include <cstring>
#include <iostream>
#define N 35
#define OVER 7
typedef unsigned long long ll;
using namespace std;
char B[N];
char A[N];
int dp[N][10][OVER+1];
//状态转移函数
int next(int now,int p,int q)
{
switch(now)
{
case 0:return p>0?1:OVER;
case 1:return p>0?1:p<0?2:OVER;
case 2:return p<0?3:q?4:OVER;
case 3:return p<0?3:p&&q-p?5:q?4:OVER;
case 4:return p>0?5:OVER;
case 5:return p<0?6:p>0?5:OVER;
case 6:return p<0?6:OVER;
default:return OVER;
}
}
//dp预处理,dp[i][j][k]为i+1位的数字,最高位为j,最高位在山的k位置,最后一位在山的最后位置,所有位上数字和的最大值。
void DP()
{
//为了防止非法状态的转移使得非法状态有解,故使初始值都为一个很大的负数。
memset(dp,128,sizeof(dp));
//处理山的最后位置
for(int i = 0 ; i < 10 ; i++)
dp[0][i][OVER-1] = i;
//往山前处理
for(int i = 1 ; i < N ; i++)
for(int j = 0 ; j < 10 ; j++)
for(int l = 0 ; l < OVER ; l++)
for(int k = 0 ; k < 10 ; k++)
dp[i][j][l] = max(dp[i][j][l] , dp[i-1][k][next(l,k-j,k)]+j);
}
int find(ll a,ll b)
{
//将b转化成字符串放到B里
sprintf(B,"%I64u",b);
int len = strlen(B);
//将a转化为字符串放到A里,并使得A与B长度一致,不足的补0
sprintf(A,"%0*I64u",len--,a);
//sum为前缀的和,now为前缀的状态
int ii,sum = 0,now;
//处理A、B相同前缀
for(ii = 0 ; A[ii] && A[ii]==B[ii] ; ii++)
{
now = !ii ? 0 : next(now,A[ii]-A[ii-1],A[ii]-'0');
sum += A[ii]-'0';
}
//tsum、tnow、暂存sum和now的状态,以备处理a部分使用,ans初值要注意(这里WA了好几次)
int tsum = sum, tnow = now, ans = 0;
for(int i = ii; B[i] ; i++)
{
for( int j = i==ii? A[i] - '0' + 1 : 0 ; j < B[i] - '0' ; j++)
ans = max(ans,dp[len-i][j][!i ? 0 : next(now,j-B[i-1]+'0',j)]+sum);
now = !i ? 0 : next(now,B[i]-B[i-1],B[i]-'0');
sum += B[i]-'0';
}
if(!ii)
{
while(A[ii]=='0')
{
ii++;
if(!A[ii])return ans;
for(int i = A[ii]=='0'?1:A[ii]-'0'+1 ; i < 10 ; i++)
ans = max(ans,dp[len-ii][i][0]);
}
now = 0;
}else now = next(tnow,A[ii]-A[ii-1],A[ii]-'0');
sum = tsum + A[ii] - '0';
for(int i = ++ii;A[i]; i++)
{
for( int j = A[i] - '0' + 1 ; j < 10 ; j++)
ans = max(ans,dp[len-i][j][next(now,j-A[i-1]+'0',j)]+sum);
now = next(now,A[i]-A[i-1],A[i]-'0');
sum += A[i] - '0';
}
return ans;
}
int main()
{
int t,cas = 1;
ll a,b;
scanf("%d",&t);
DP();
while(t--)
{
scanf("%I64u %I64u",&a,&b);
if(!a)a++;
if(!(b+1))b--;
printf("Case %d: %d\n",cas++,find(a-1,b+1));
}
return 0;
}
注意比较坑爹的I64u与llu。。。。。