WUSTOJ 1287: B304(Java:355ms,C:8ms)

108 篇文章 0 订阅
80 篇文章 18 订阅
题目:?1287: B304
参考:?WUST OJ 1287:B304(DP)——Mitsuha_
题目?
  • 有一间教室,里面有NM列桌子(NM键盘输入)
  • 每个桌子上最多有一朵?(0朵花或者1朵花),?的总数量不限
  • 每行和每列的?的数量都只能为奇数
  • 最多有100行100列桌子
  • 问题:有NM列桌子,?的摆放方法有多少种?
分析

感谢参考博客,因为我是直接拿参考博客的代码运行,通过大量数据总结的规律。
实际上,这种题做的少的话,不容易找到规律(不信可以试试?)。

?画表格分析(带下划线的数据?参考博客代码运行出来的,如果你能算出来,评论区讨论✒️,给你点赞?表示佩服)

N \ M123456
1101010
20208032
3101602560
4080512032768
5102560655360
6032032768033554432
  • 最先想到的应该是对称矩阵,因此代码中,我们只需要初始化右上三角部分即可(注:文末有思考题,看完再思考。)
  • 其次,可以发现规律:NM奇偶性不同的时候,方法种数必为0。(能手画总结出来)

看完表格可能就发现所有的数都是2n(n为自然数)的形式,n的取值情况如?,不考虑上面表格中值为0的情况,不考虑左下三角的情况:

N \ M123456
1000
2135
348
4915
516
625

可以看(连猜带蒙)出

1行的公差d1为	2 * (1 - 1)2行的公差d2为	2 * (2 - 1)3行的公差d3为	2 * (3 - 1),待验证
第4行的公差d4为	2 * (4 - 1),待验证
...
第i行的公差di为	2 * (i - 1)
...

递推公式:di = 2 * (i - 1)

你会想,要是不同行之间也有规律就好了。你别说还真有,请看对角线数据,注:第1个1是初始化的,不在表格中

 0 =  1 + 2 * (1 - 1) - 1
 1 =  0 + 2 * (2 - 1) - 1
 4 =  1 + 2 * (3 - 1) - 1
 9 =  4 + 2 * (4 - 1) - 1
16 =  9 + 2 * (5 - 1) - 1
25 = 16 + 2 * (6 - 1) - 1

由于di = 2 * (i - 1),故
 0 =  1 + d1 - 1
 1 =  0 + d2 - 1
 4 =  1 + d3 - 1
 9 =  4 + d4 - 1
16 =  9 + d5 - 1
25 = 16 + d6 - 1

递推公式:an = an-1 + dn - 1
递推公式都有了,现在代码写起来简单了吧!

Java
/**
 * Time 355ms
 * @author wowpH
 * @version 2.2
 * @date 2019年5月23日下午6:56:10
 * Environment:	Windows 10
 * IDE Version:	Eclipse 2019-3
 * JDK Version:	JDK1.8.0_112
 */

import java.util.Scanner;

public class Main {
	private static final int MOD = 1000000007;	// 取模
	private static final int MAX_NM = 101;		// 桌子最多100行100列

	private int[][] ways;	// n行m列桌子有ways[n][m]种方法,下标从1开始

	public Main() {
		init();		// 初始化

		int m, n;
		Scanner sc = new Scanner(System.in);
		while (sc.hasNext()) {
			n = sc.nextInt();	//行数
			m = sc.nextInt();	//列数
			// 只初始化右上三角部分,故需要判断
			if (n < m) {
				System.out.println(ways[n][m]);
			} else {
				System.out.println(ways[m][n]);
			}
		}
		sc.close();
	}

	private void init() {
		ways = new int[MAX_NM][MAX_NM];	// 下标从1开始
		int diagonal = 1;	// 对角线方法种数的指数(注:初始化为1,见分析)
		int d;	// 公差,第k+2列与第k列的方法种数的指数的差

		// 计算第i行的方法种数
		for (int i = 1; i < MAX_NM; i++) {
			d = 2 * (i - 1);	// 第i行桌子的公差
			diagonal += d - 1;	// ways[i][i]的指数等于左上角位置的指数加(d-1)

			ways[i][i] = 1;		// 初始i行i列桌子的方法数为1
			// 计算i行i列桌子的方法种数
			for (int j = 0; j < diagonal; j++) {
				ways[i][i] = (ways[i][i] << 1) % MOD;
			}

			// 计算第i行第j列的方法种数
			for (int j = i + 2; j < MAX_NM; j += 2) {
				// 初始第j列的方法种数为第j-2列的方法种数
				ways[i][j] = ways[i][j - 2];
				// 第j列是第j-2列的【2的d次方】倍,左移d次
				for (int k = 0; k < d; k++) {
					ways[i][j] = (ways[i][j] << 1) % MOD;
				}
			}
		}
	}

	public static void main(String[] args) {
		new Main();
	}
}
C(注释见Java版)
/**
 * Time 8ms
 * @author wowpH
 * @version 1.1
 * @date 2019年5月24日17:24:20
 */

#include<stdio.h>

#define MAX_NM 101
#define MOD 1000000007

int ways[MAX_NM][MAX_NM];

void init() {
	int diagonal = 1;
	int d;

	for (int i = 1; i < MAX_NM; i++) {
		d = 2 * (i - 1);
		diagonal += d - 1;

		ways[i][i] = 1;
		for (int j = 0; j < diagonal; j++) {
			ways[i][i] = (ways[i][i] << 1) % MOD;
		}

		for (int j = i + 2; j < MAX_NM; j += 2) {
			ways[i][j] = ways[i][j - 2];
			for (int k = 0; k < d; k++) {
				ways[i][j] = (ways[i][j] << 1) % MOD;
			}
		}
	}
}

int main() {
	init();

	int m, n;
	while (scanf("%d%d", &n, &m) != EOF) {
		if (n < m) {
			printf("%d\n", ways[n][m]);
		}
		else {
			printf("%d\n", ways[m][n]);
		}
	}
	return 0;
}

注释如此详细,应该能看懂吧!
思考题:为什么初始化右上角部分,而不是左下角部分?(大佬跳过)
答:现在假设你的程序已经计算出了第一个数[1][1],因此题目演变成你现在要计算[1][3]还是[3][1]的问题?很明显,计算[1][3]只需要移动1位,而计算[3][1]需要移动12位,其他数据同理,前者块,后者慢。
结论:访问二维数组的时候,尽量将移动距离大的(一维)放在外层循环,移动距离小的(二维)放在内层循环。不仅限于此,其他地方也可以这么思考。

如有帮助,还望?支持,谢谢!

备注:第一次发现可以贴表情,所以用的有点多,见谅!


版权声明:

  1. 转载请于首页注明链接形式的WUSTOJ 1287: B304(Java)——wowpH
  2. 代码原创,公开引用不能删除首行注释(作者,版本号,时间等信息);
  3. 如果有疑问欢迎评论区留言,尽量解答;
  4. 如果有错误,还望大侠评论区指正。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值