hdu5898 (digital dp)

Topic link: Click to open the link

The general meaning of the question is to give you an interval [l,r], and let you count how many numbers there are in this interval. For each consecutive odd number, the number of numbers in this segment is even, and for each consecutive segment Even numbers, the number of numbers in the segment is odd ( 1<=L<=R<= 9*10^18)

Idea: For this kind of subject that constrains digital numbers and counts numbers at the same time, it should be quickly thought of using digital dp to do it. Paste the code first:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

class Reader{
	static BufferedReader reader;
	static StringTokenizer tokenizer;
	static void init(InputStream input)
	{
		reader=new BufferedReader(new InputStreamReader(input));
		tokenizer=new StringTokenizer("");
	}
	static String next() throws IOException{
		while (!tokenizer.hasMoreTokens())
			tokenizer=new StringTokenizer(reader.readLine());
		return tokenizer.nextToken();
	}
	static int nextInt() throws IOException{
		return Integer.parseInt(next());
	}
	static long nextLong() throws IOException{
		return Long.parseLong(next());
	}
}
public class Main {
	static int t;
	static String str1,str2;
	static char ch1[],ch2[];
	static long l,r,ans1,ans2;
	static long f[][][];
	static long dfs(char ch[],int front,int nown,boolean flag,boolean lead,boolean islimit)
	{
		int type=flag?1:0;
		if (nown==ch.length)
		{
			if ((flag)&&(front==0)&&(!lead)) return 1;
			return 0;
		}
		if ((!lead)&&(!islimit))
		{
			if (f[front][nown][type]!=-1)
				return f[front][nown][type];
		}
		int end=islimit?(ch[nown]-'0'):9;
		boolean bo,bo2,bo3;
		long num1,num2,res;
		num1=num2=0;
		for (int i=0;i<=end;i++)
		{
			bo=lead&&(i==0);
			bo2=islimit&&(i==end);
			if (front==0) {
				if (i%2==1)
				{
					if (flag)
						num1+=dfs(ch, front, nown+1, false,false,bo2);
					else
					{
						num1+=dfs(ch, front, nown+1, true, false, bo2);
						num2+=dfs(ch, 1-front, nown+1, false, false, bo2);
					}
				}
			}
			else {
				if (i%2==0)
				{
					if (flag)
						num1+=dfs(ch, front, nown+1, false,false,bo2);
					else
					{
						if (bo)
							num1+=dfs(ch, front, nown+1, false, bo, bo2);
						else
						    num1+=dfs(ch, front, nown+1, true, bo, bo2);
						num2+=dfs(ch, 1-front, nown+1, true, bo, bo2);
					}
				}
			}
		}
		res = num1 + num2;
		if ((!lead)&&(!islimit)) f[front][nown][type]=res;
		return res;
	}
	static void initarr() {
		f=new long[2][20][2];
		for (int i=0;i<=1;i++)
			for (int j=0;j<=19;j++)
				for (int k=0;k<=1;k++)
					f[i][j][k]=-1;
	}
	static void deal() {
		l--;
		ch1=String.valueOf(l).toCharArray();
		ch2=String.valueOf(r).toCharArray();
		initarr ();
		ans1=dfs(ch1, 0, 0, true, true, true);
		initarr ();
		ans1+=dfs(ch1,1,0,false,true,true);
		initarr ();
		ans2=dfs(ch2, 0, 0, true, true, true);
		initarr ();
		ans2+=dfs(ch2,1,0,false,true,true);
		ans2=ans2-ans1;
		System.out.println(ans2);
	}
	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		Reader.init(System.in);
		t=Reader.nextInt();
		for (int casenum = 1; casenum <= t; casenum ++)
		{
			l=Reader.nextLong();
			r=Reader.nextLong();
			System.out.print("Case #"+casenum+": ");
			deal();
		}
	}

}

First think of the number of such numbers in [l,r], which is the number of such numbers in [1,r] minus the number of such numbers in [1,l-1], so continue to consider after simplification, The main part of the digital dp is as follows (the digital dp has not been written much before, the code is a bit long and ugly...)

static long dfs(char ch[],int front,int nown,boolean flag,boolean lead,boolean islimit)
	{
		int type=flag?1:0;
		if (nown==ch.length)
		{
			if ((flag)&&(front==0)&&(!lead)) return 1;
			return 0;
		}
		if ((!lead)&&(!islimit))
		{
			if (f[front][nown][type]!=-1)
				return f[front][nown][type];
		}
		int end=islimit?(ch[nown]-'0'):9;
		boolean bo,bo2,bo3;
		long num1,num2,res;
		num1=num2=0;
		for (int i=0;i<=end;i++)
		{
			bo=lead&&(i==0);
			bo2=islimit&&(i==end);
			if (front==0) {
				if (i%2==1)
				{
					if (flag) 
						num1+=dfs(ch, front, nown+1, false,false,bo2);
					else 
					{
						num1+=dfs(ch, front, nown+1, true, false, bo2);
						num2+=dfs(ch, 1-front, nown+1, false, false, bo2);
					}
				}
			}
			else {
				if (i%2==0)
				{
					if (flag) 
						num1+=dfs(ch, front, nown+1, false,false,bo2);
					else 
					{
						if (bo) 
							num1+=dfs(ch, front, nown+1, false, bo, bo2);
						else 
						    num1+=dfs(ch, front, nown+1, true, bo, bo2);
						num2+=dfs(ch, 1-front, nown+1, true, bo, bo2);
					}
				}
			}
		}
		res=num1+num2;
		if ((!lead)&&(!islimit)) f[front][nown][type]=res;
		return res;
	}

之前数位dp看的这里:https://blog.csdn.net/wust_zzwh/article/details/52100392,不过还是有点浮躁,看过后也没怎么进行练习,所以这道基本就是我写的第一道比较标准点的数位dp了。。。记忆化搜索这种东西我还是用的比较少,然后数位dp又有点自己的标准,所以大家初涉数位dp还是希望琢磨透了多去上手练练。

这里nown是表示当前考虑第(nown+1)位的数(这里最高位是第一位)front的0和1分别代码当前位选奇数还是偶数,flag表示之前奇数数码或者偶数数码是否已经满足题目所要求性质,lead和islimit的含义,上面的链接里有讲解,然后相应的bo和bo2的含义也可得知。

这里做法主要是枚举当前位数码i,然后针对front进行讨论

front=0,即当前位选奇数数码,判断i后,内部代码如下

if (flag) 
	num1+=dfs(ch, front, nown+1, false,false,bo2);
else 
{
	num1+=dfs(ch, front, nown+1, true, false, bo2);
	num2+=dfs(ch, 1-front, nown+1, false, false, bo2);
}

如果前面的奇数数码已经有偶数个,满足了性质,由于当前位又选了奇数数码,所以接下来只能再选奇数数码;

而如果前面的奇数数码有奇数个,未满足题目要求性质,当前位选了奇数数码后自然就满足了,接下来就有两种策略:继续选奇数数码,或者直接结束该连续段然后转而选择偶数数码(此时注意,连续偶数数码9段要有奇数个偶数,所以要出现该偶数段但还没选偶数,也就是连续偶数数码个数是0个时,肯定是不满足性质的(*))


front=1,即当前位选偶数数码,判断完i后,内部代码如下

if (flag) 
	num1+=dfs(ch, front, nown+1, false,false,bo2);
else 
{
	if (bo) 
	    num1+=dfs(ch, front, nown+1, false, bo, bo2);
	else 
	    num1+=dfs(ch, front, nown+1, true, bo, bo2);
	num2+=dfs(ch, 1-front, nown+1, true, bo, bo2);
}
这里跟front=0的情形大体还是比较像的,主要注意接下来转移选奇数数码时,由于连续奇数数码段要有偶数个奇数,连续奇数数码个数是0个,是满足性质的;还有一点,如果当前位选完了还处于前导0状态的话,那么之后基本还是(*)的情形



Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326754659&siteId=291194637