hihoCoder太阁最新面经算法竞赛题解(7)

题目来源:

    HihoCoder1318

题目要求:

    如果一个二进制数包含连续的两个1,我们就称这个二进制数是非法的。小Hi想知道在所有 n 位二进制数(一共有2^n个)中,非法二进制数有多少个。例如对于 n = 3,有 011110111 三个非法二进制数。

    由于结果可能很大,你只需要输出模10^9+7的余数。
 
 

解答:

    本题是典型的动态规划的题目,对于i位二进制下的非法二进制数的个数,我们通过i-1位二进制数下的情况来进行求解。具体方法是:定义valid[0][i]表示i位二进制数下,并且最后一位为0的合法二进制数的个数;valid[1][i]表示i位二进制数下,最后一位是1的合法二进制数的个数;同时定义invalid[i]i位二进制数中的非法二进制数的个数。
    根据题目要求,当二进制数中存在两个连续的1时为非法二进制数。因此,在计算i位二进制数中的非法二进制数时,考虑如下3种情况:

    (1) 对于i-1位二进制数中最后一位是1的合法二进制数,再其后再增加一个1,就会成为一个i为的非法二进制数,根据上文中的定义,此情况的非法二进制数的数目是:valid[1][i-1] 
    (2) 对于i-1位二进制数中最后一位是0的合法二进制数,再其后无论添加0或者1,都不会产生非法的二进制数。 
    
(3) 对于i-1位二进制数中的所有非法二进制数,在其后添加01,得到的都是i位长度的非法二进制数,此情况的非法二进制数的数目是:2 * invalid[i-1]。    
分析以上三种情况,可以得到invaid[i]的递推公式:
                        invalid[i] = valid[1][i-1] + 2 * invalid[i-1] 
    对于合法二进制数的数目,也可以用类似的分析方法。显然,一个i位的合法二进制数删去最后一位后,得到的一定是一个i-1为的合法二进制数。因此,对于i位长度最后一位是1的合法二进制数,删去末尾的1后,是一个i-1位的合法二进制数,并且,该二进制数最后一位一定是0,因为如果是1,再添加1就会有两个连续的1,而构成非法二进制数。所以:
                        valid[1][i] = valid[0][i-1]
而对于i位长度最后一位是0的合法二进制数,则无此限制,去掉末尾的0后,只要能保证剩余的数字是合法二进制数即可,因此:
                        valid[0][i] = valid[0][i-1] + valid[1][i-1]
    得到递推公式后,我们还需要一个递推的起点,这个起点是显而易见的,对于1位二进制数,最后一位为0和1的合法二进制数各有一个,不存在非法二进制数,因此:
              valid[0][1] = 1     valid[1][1] = 1     invalid[1] = 0
    对于更一般的情况,代入递推公式计算即可。
    以上为本题解法,对于输入数据n,计算invalid[n]就是本题的答案。 

输入输出格式:
 
   输入:

一个整数n(1≤n≤100)。

输出:
    n位非法二进制数的数目模109+7的余数。

程序代码:

package hihocoder;

import java.util.Scanner;

/**
 * This is the ACM problem solving program for hihoCoder 1318.
 * 
 * @version 2017-04-29
 * @author Zhang Yufei.
 */
public class HihoCoder1318 {
	/**
	 * Mod Constant.
	 */
	private static final int MOD = 1000000007;

	/**
	 * Used for dp.
	 */
	private static int[][] dp;

	/**
	 * Used for dp.
	 */
	private static int[] dp2;

	/**
	 * Input data;
	 */
	private static int n;

	/**
	 * The main program.
	 * 
	 * @param args
	 *            The Command line parameters list.
	 */
	public static void main(String[] args) {
		dp = new int[101][2];
		dp2 = new int[101];
		
		dp[1][0] = 1;
		dp[1][1] = 1;
		
		for(int i = 2; i <= 100; i++) {
			long data = (long) dp[i - 1][0] + dp[i - 1][1];
			data %= MOD;
			dp[i][0] = (int) data;
			dp[i][1] = dp[i - 1][0];
		}
		
		dp2[1] = 0;
		for(int i = 2; i <= 100; i++) {
			long data = (long) dp2[i - 1] * 2 + dp[i - 1][1];
			data %= MOD;
			dp2[i] = (int) data;
		}
		
		Scanner scan = new Scanner(System.in);
		n = scan.nextInt();
		scan.close();
		
		System.out.println(dp2[n]);
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值