SGU_390_Tickets(另類數位DP)

Tickets

Time Limit : 1000/500ms (Java/Other)   Memory Limit : 524288/262144K (Java/Other)
Total Submission(s) : 79   Accepted Submission(s) : 16
Problem Description


Conductor is quite a boring profession, as all you have to do is just to sell tickets to the passengers. So no wonder that once upon a time in a faraway galaxy one conductor decided to diversify this occupation. Now this conductor sells several tickets at a time to each passenger. More precisely, he successively gives tickets to the passenger until the sum of the digits on all the tickets given becomes not less than some integer number k. Then this process repeats for the next passenger. Initially conductor has a tape of tickets numbered successively from l to r, inclusive. This way of tickets distribution is quite good, because passengers are glad to get several tickets when they pay only for one. But there is one disadvantage. Since each passenger gets several tickets, it is possible that conductor won't be able to serve all passengers. Your task is to help conductor in this difficult situation. You should calculate how many passengers is the conductor able to serve.

Input
Input file contains three integer numbers lr and k (1 ≤ l ≤ r ≤ 1018, 1 ≤ k ≤ 1000).

Output
Output should contain exactly one number — the answer to the problem.

Example(s)
sample input

sample output

40 218 57

29



題意:
給你一個區間,讓你分段,分段完後一段連續的數的數位和要大於K,問這樣的段有多少
題解:
樸素的數位DP肯定不能過,這題在這篇國家集訓隊論文上出現過,作者講的比較清楚(算法合集之《淺談數位類統計問題》),
我們設dp[i][sum][rem]表示考慮到第i爲,i之前的數位和爲sum,當前還有多少rem沒有用來分段,這裏我用一個pair型來記錄滿足條件的段數cnt,和在這個dp狀態下的rem,然後採用記憶化搜索,就能大大降低時間複雜度,爲什麼?因爲當考慮到第i位時,前面的和爲sum,只要不是邊界情況下後面的每一個數位都可以取0-9,所以如果之前算出了當前的這個狀態,那麼就可以直接返回這個狀態的值,這就是記憶化搜索的優勢。
我看完論文後,對rem的概念還是比較模糊,然後自己模擬了一遍,最後懂了這個的意思,就拿樣列來說,40—218內找滿足條件的分段數,我們最開始sum和rem都爲0,從40開始搜,40的數位和sum爲4,然後dfs到最後一位,發現sum+rem小於57,所以到40這個數的時候不能分爲一段,然後返回cnt=0,rem=sum+rem=4,就代表40這個數還沒用於分段,然後回溯回去搜41,此時的sum爲5,rem爲4,然後發現sum+rem還是小於57,就返回cnt=0,rem=sum+rem=9,代碼40和41這兩個數都沒用於分段,最後一直搜到47的時候sum=11,rem=49,然後發現sum+rem>=57成立,返回cnt=1,rem=0,表示到這個數可以分爲一段,然後rem爲0表示分完一段後,沒用的數,沒有了,所以對應的數位和rem爲0。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define F(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long LL;
typedef pair<LL,LL>P;
int l[30],r[30],ln,rn,vis[30][200][1005];
LL a,b,m;
P dp[30][200][1005];
P dfs(int pos=rn,int sum=0,int rem=0,bool up=1,bool dn=1){
	if(!pos)if(sum+rem>=m)return P(1,0);else return P(0,sum+rem);
	if(vis[pos][sum][rem]&&!up&&!dn)return dp[pos][sum][rem];
	int st=dn?l[pos]:0,end=up?r[pos]:9;P ans=P(0,rem);
	F(i,st,end){
		P tp=dfs(pos-1,sum+i,ans.second,up&&i==end,dn&&i==st);
		ans.first+=tp.first,ans.second=tp.second;
	}
	if(!up&&!dn)dp[pos][sum][rem]=ans,vis[pos][sum][rem]=1;
	return ans;
}

int main(){
	while(~scanf("%I64d%I64d%I64d",&a,&b,&m)){
		memset(vis,0,sizeof(vis));
		for(rn=0;b;b/=10)r[++rn]=b%10;
		for(ln=0;ln<rn;a/=10)l[++ln]=a%10;
		printf("%I64d\n",dfs().first);
	}
	return 0;
}




 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章