方块游戏—题解

 1590 - 方块游戏


Time Limit: 1s Memory Limit: 128MB


Submissions: 33 Solved: 13 
DescriptionBG和ZZ一起玩一个游戏,游戏规则如下:
       游戏开始时画在一张纸上,纸上画有n*m个方块组成的格子,BG和ZZ轮流玩这个游戏,BG先开始。每一轮玩家都会在上一次的方框内框出一个小方框作为下一轮的方框,这个小方框的边和原先的方框的边不能够有交集。
       这个游戏没有赢家,BG和ZZ只是不断的缩小方框,直到游戏进行k轮。现求游戏有多少种方法。(即游戏从初始状态到进行k轮后的任意状态的路径数目)。Input输入包括多组数据,每组数据为一行,每行有3个整数n,m,k(其中1 ≤ n, m, k ≤ 1000)。Output每组数据输出一行,每行为一个整数ans,表示游戏的方法数量,由于数量可能很大,请输出答案对1000000007求余的结果。
Sample Input3 3 1
4 4 1
6 7 2

Sample Output1
9
75


方法一:(模拟)
import java.util.Scanner;

public class Asist
{
	/**   
	 *   (1)
	 *    dp1[i][j] 代表  (i+2) * (j+2)个方块组成的格子进行 1 轮的总方法数 !(数学归纳法获知的!——可以打印出来看看)
	 *    
	 *   (2)
	 *    关于这个式子:sum=sum+test(k-1, i-2, j-2)*(n-i+1)*(m-j+1);
	 *      a) test(k, i, j) 求的是  i * j 个方块组成的格子进行k轮的总可行数! (废话了!)
	 *      
	 *      b) 至于这个式子的来历,我举个实际例子吧,(一般我的解题思路都是先举出一个实际例子、然后对其他情况加以证明而完成的、)
	 *   
	 *    1. 对于给定的n, m, k, 必须满足  n >= 2k+1 && m >= 2k+1才可有可行方案,否则输出 0;  (很容易证明, 也是关键点!)
	 *    
	 *    例子:
	 *         n  m  k
	 *         7  8  3   
	 *     说明:  每进行一轮时, 此时方框的最外层是不可取的, 也就是说,实际最大可取方框为 (n-2) *  (m-2)
	 *     
	 *    第一轮, 应至少取出   x * y 个方框,(其中 n >= x >= 2*k - 1 && m >= y >= 2*k - 1  ),此时 n = 5, m = 6, k = 3,
	 *           这样做方面配合我的 dp1[][]数组,才这样的。
	 *         这一轮的可行取法有:    
	 *                           i,j
	 *                       1.  5,5      这种取法的方式有 2 次       ——   
	 *                                                        次数  = (n-i+1) * (m-j+1) (很容易证明)
	 *                       2.  5,6      这种取法的方式有 1次       ——
	 *                       
	 *    第二轮, 根据前一轮的答案,同理的做法。 第一轮中有两种符合的情况:
	 *      1.   n  m  k   
	 *           5  5  2
	 *        完成一轮操作, 应至少取出   x * y 个方框,(其中 n >= x >= 2*k - 1 && m >= y >= 2*k - 1  ),此时 n = 3, m = 3, k = 2,
	 *      这种情况,这一轮的取法有:
	 *                      i, j
	 *                   1. 3, 3  这种取法的方式有1次
	 *      2.  n  m  k    
	 *          5  6  2
	 *        完成一轮操作, 应至少取出   x * y 个方框,(其中 n >= x >= 2*k - 1 && m >= y >= 2*k - 1  ),此时 n = 3, m = 4, k = 2,
	 *      这种情况,这一轮的取法有:
	 *                      i, j
	 *                   1. 3, 3  这种取法的方式有2次 
	 *                   2. 3, 4 这种取法的方式有1次
	 *     第三轮,根据前一轮的答案,同理
	 *      1.  n  m  k   
	 *          3  3  1
	 *         当 k = 1时,就好说了, 因为我的dp1[] 就是解决 k=1的。  此时n = 1, m = 1, k = 1,
	 *         结果是: dp[1][1] * 1(第三轮1次) * 1(第二轮1次) * 2(第一轮2次) = 2
	 *      2.  n  m  k   
	 *          3  3  1
	 *         结果是: dp[1][1] * 1(第三轮1次) * 2(第二轮2次) * 1(第一轮1次) = 2
	 *      3.  n  m  k
	 *          3  4  1
	 *         结果是: dp[1][2] * 1 * 1 * 1 = 3;
	 *         
	 *       综上所述:   sum = 2 + 2 + 3 = 7;
	 */ 
	public static final int MOD = 1000000007;
	public static int dp1[][];                            // 记录一个初始状态
	public static long dp2[][] = new long[1000][1000];    // 记忆化搜索
	public static void main(String[] args)
 	{ 
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int m = sc.nextInt();
		int k = sc.nextInt();
		
		// 初始化 dp1 数组
		 dp1 = new int[1000][1000];
		for (int i = 1; i < 1000 ; i++)
		{
		    dp1[1][i] = i * (i+1) / 2;  
		    dp1[i][1] = dp1[1][i];
		}
		for (int i = 2; i < 1000; i++)
		{
			for (int j = 2; j < 1000; j++)
			{
			   dp1[i][j] = dp1[i][1] * dp1[1][j];
			}
		}
		
	   //模拟游戏
	    System.out.println(test(k, n-2, m-2));
 	}
	
	private static long test(int k, int n, int m)
	{
		if (k == 1)
		{
			return dp1[n][m];
		}
		if (dp2[n][m]!=0) return dp2[n][m];
		
		long sum = 0;
		int temp = 2*k-1;
		
		for (int i = temp; i <= n; i++)
		{
			for (int j = temp; j <= m; j++)
			{
				sum = (sum + (test(k-1, i-2, j-2)%MOD * (n-i+1)%MOD * (m-j+1)%MOD) % MOD)%MOD;
			}
		}
		
		dp2[n][m] = sum;   // 记忆
		return sum;
	}
}


方法二:(组合数学问题) 上代码
import java.util.Scanner;


public class Main
{
	public static final int MOD = 1000000007;
	public static void main(String[] args)
 	{ 
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int m = sc.nextInt();
		int k = sc.nextInt();
		
		System.out.println(test(2*k,n-1)*test(2*k,m-1)%MOD);
 	}
	
	private static long test(int n, int m)
	{
         if (n > (m / 2)) n = m-n;
         
         long sum = 1;
         for (int i = 0; i < n; i++) sum = (sum * (m-i)) % MOD;
         long sun = 1;
         for (int i = 2; i <= n; i++) sun = (sun * i) % MOD;
         
         return sum / sun;
	}
}




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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值