有N行M列的正方形盒子。每个盒子有三种状态0, -1, +1。球从盒子上边或左边进入盒子,从下边或右边离开盒子。规则:
如果盒子的模式是-1,则进入它的球从下面出去。(方向变为向下)
如果盒子的模式是+1,则进入它的球从右面出去。 (反向变为向右)
如果盒子的模式是0, 则进入它的球方向不变。从上面进入的,从下面出去,从左面进入的,从右面出去。
球离开一个盒子,这个盒子的模式切换为相反数。已知,每个盒子的状态,扔k个球,它们都从左上角那个盒子的上面进入(方向向下),问最终有几个球从右下角的盒子的下边出去。
(可以理解维球一个一个放,等待的时间足够长,不会有两个球同时进入一个盒子的情形)本题由Javaman翻译。
Input
第1行:包括3个数M, N, K中间用空格分隔,M,N 为盒子的宽度和高度,K为球的数量(1 <= M, N <= 1000, 1 <= K <= 10^18)。 第2 - N + 1行:每行M个数(-1, 0 或 1),表示对应的模式。
Output
输出1个数,对应最终有有多少个球从右下角的盒子的下边出去。
Input示例
3 2 4 -1 0 -1 1 0 0
Output示例
1
题解:设dp[0][i][j]为从i行j列向下出去球的数量,dp[1][i][j]为i行j列向右出去球的数量。则能到达i行j列只能从i行j - 1列向右,i - 1行j列向下。如果i行j列为0,则进来方向和出去方向相同。如果是1,则进来的求先向右,后向下。。很明显发现奇数情况向右比向下多1,偶数相同。如果是-1,跟1时同样讨论即可。
感觉是水题啊,我居然都做出来了。这是假的五级算法题吧!
AC代码
#include <stdio.h> #include <iostream> #include <string> #include <queue> #include <map> #include <vector> #include <algorithm> #include <string.h> #include <cmath> typedef unsigned long long ll; using namespace std; const ll maxn = 1e3 + 10; ll dp[2][maxn][maxn], a[maxn][maxn]; int main(){ ll n, m, k; scanf("%llu %llu %llu", &m, &n, &k); for(ll i = 1; i <= n; i++) for(ll j = 1; j <= m; j++) scanf("%llu", &a[i][j]); dp[0][0][1] = k; for(ll i = 1; i <= n; i++){ for(ll j = 1; j <= m; j++){ if(a[i][j] == 0){ dp[0][i][j] = dp[0][i - 1][j]; dp[1][i][j] = dp[1][i][j - 1]; } else if(a[i][j] == -1){ ll tmp = dp[0][i - 1][j] + dp[1][i][j - 1]; dp[1][i][j] = tmp / 2; dp[0][i][j] = tmp - dp[1][i][j]; } else{ ll tmp = dp[0][i - 1][j] + dp[1][i][j - 1]; dp[0][i][j] = tmp / 2; dp[1][i][j] = tmp - dp[0][i][j]; } } } printf("%llu\n", dp[0][n][m]); return 0; }