不知为何,我感觉数位dp和带权并查集好像,两个无论原理还是做法都没什么关系,但我感觉思想有一丝联系,都是通过一种基础的算法延伸出来的,并且不同的题目虽然实现不一致,但只要摸清那个关系,之后就都是固定的套路了
不多说,上题:
.HDU-3555
Bomb
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)Total Submission(s): 16706 Accepted Submission(s): 6124
Problem Description
The counter-terrorists found a time bomb in the dust. But this time the terrorists improve on the time bomb. The number sequence of the time bomb counts from 1 to N. If the current number sequence includes the sub-sequence "49", the power of the blast would add one point.
Now the counter-terrorist knows the number N. They want to know the final points of the power. Can you help them?
Now the counter-terrorist knows the number N. They want to know the final points of the power. Can you help them?
Input
The first line of input consists of an integer T (1 <= T <= 10000), indicating the number of test cases. For each test case, there will be an integer N (1 <= N <= 2^63-1) as the description.
The input terminates by end of file marker.
The input terminates by end of file marker.
Output
For each test case, output an integer indicating the final points of the power.
Sample Input
3 1 50 500
Sample Output
0 1 15HintFrom 1 to 500, the numbers that include the sub-sequence "49" are "49","149","249","349","449","490","491","492","493","494","495","496","497","498","499", so the answer is 15.
Author
fatboy_cw@WHU
Source
Recommend
zhouzeyong
想说的都在注释里
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
long long int dp[30][3];
//dp[i][0]存的是i位里包含49的数的个数
//dp[i][1]存的是i位里不包含49并且首位是9的数的个数
//dp[i][2]存的是i位里不包含49的数的个数(所以dp[i][1]是他的一个子集)
//如果让我去想为什么要用这三个数组,我觉得dp[i][0]的作用最明显,题目要求的就是包含49的个数,肯定要用到
//dp[i][1]我感觉是个辅助作用,方便向下去求另外两个数组的值
//dp[i][2]的作用很重要也很难理解,一是向下求dp[i][1]的值,二是为了在求解时当前面已经有49时,后面就可以任意取了
//当然第二个作用完全可以用别的方法实现,求dp[i][2]也可以这样求
/* long long int t=10;
for(i=1;i<20;i++)
{
dp[i][0]=10*dp[i-1][0]+dp[i-1][1];
dp[i][1]=dp[i-1][2];
dp[i][2]=t-dp[i][0];
t*=10;
printf("%lld\n",dp[i][2]);
}
*/
int init()
{
int i;
memset(dp,0,sizeof(dp));
dp[0][2]=1;
for(i=1;i<20;i++)
{
dp[i][0]=10*dp[i-1][0]+dp[i-1][1];//上一位已经包含49的乘10 加上 上一位首位为9这一位为4的情况
dp[i][1]=dp[i-1][2];
dp[i][2]=10*dp[i-1][2]-dp[i-1][1];//上一位不包含49的乘10 减去 上一位首位是9这一位是4的
//发现dp[i][0]+dp[i][2]=10*(dp[i-1][0]+dp[i-1][2])所以其实这俩就是个对立事件
}
return 0;
}
int main()
{
long long int n,ans,bit[30];
int i,t;
scanf("%d",&t);
init();
while(t--)
{
ans=0;
scanf("%lld",&n);
n++;//对于为什么要n++,我想了很久,因为下面的做法没有算n,可以人脑跟着跑一遍就知道了
int len=1;
while(n)
{
bit[len++]=n%10;
n/=10;
}//按位存到一个数组里
bit[len--]=0;
int flog=1;
//最精巧的就是这部分,如何处理dp数组来得到答案是数位dp的关键
for(i=len;i>=1;i--)
{
ans+=bit[i]*dp[i-1][0];//每次加上位的值乘以小一位含的所有有49的个数
//为什么每次都要加呢,举个例子就清楚了,5495,第一次加的是5*dp[2][0],5代表这一位是0,1,2,3,4这五个数
//而这一位是5的情况 ,会在下一次循环加,第二次循环里加的是4*dp[1][0],4代表这一位是0,1,2,3,其实此时第一位就都是5
//肯定会有人发现这样会落下一种情况,那就是49xx,还有549x,5449,所以就要用下面的处理
if(!flog)
{
ans+=dp[i-1][2]*bit[i];
}
else if(bit[i]>4)
{
ans+=dp[i-1][1];
}
//当出现49这种情况,后面就可任意了,这里为了方便写,
//就先加上了后面包含49的,又加上了后面不包含49的,改成判断条件整体求也可
if(bit[i]==9&&bit[i+1]==4)
{
flog=0;
}
}
printf("%lld\n",ans);
}
return 0;
}