51Nod 1293 球与切换器 DP分类
基准时间限制:1 秒 空间限制:131072 KB
有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
可以整体考虑,因为落下一个,就会改变当前的值为相反数,所以一方为n+1/2,一方为n/2
类似考虑,当为+1时,优先考虑对下个右方格子的影响,-1时优先考虑下个左方格子的影响,为0时直接加和
代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 int dir; 4 long long dp[1002][1001][2]; 5 long long read(){ 6 long long res = 0; 7 int flag = 0; 8 char ch; 9 if ((ch = getchar()) == '-'){ 10 flag = 1; 11 } 12 else if(ch >= '0' && ch <= '9'){ 13 res = ch - '0'; 14 } 15 while ((ch = getchar()) >= '0' && ch <= '9'){ 16 res = res * 10 + (ch - '0'); 17 } 18 return flag ? -res : res; 19 } 20 int main() { 21 int n,m; 22 long long k; 23 m=read();n=read();k=read(); 24 //用0来表示-1朝下,1来表示+1朝右 25 //刚开初始放下为朝下,全部球落入dp[1][1][0],dp[1][1][1]=0; 26 dp[1][1][0]=k; 27 for(int i=1;i<=n;++i) { 28 for(int j=1;j<=m;++j) { 29 //不同再开数组保存方向,因为一行一行处理没有后效性 30 dir=read(); 31 long long sum=dp[i][j][0]+dp[i][j][1];//当前关口的总数 32 if(dir==0) { 33 dp[i][j+1][1]+=dp[i][j][1];//朝右j+1 34 dp[i+1][j][0]+=dp[i][j][0];//朝下i+1 35 } else if(dir==-1) { 36 dp[i+1][j][0]+=(sum+1)>>1;//先计算朝下的,i+1,状态0 37 dp[i][j+1][1]+=sum>>1;//剩余的朝右j+1,状态1 38 } else if(dir==1) { 39 dp[i][j+1][1]+=(sum+1)>>1;//先计算朝右的,j+1,状态1 40 dp[i+1][j][0]+=sum>>1;//剩余的朝右i+1,状态0 41 } 42 } 43 } 44 printf("%lld\n",dp[n+1][m][0]);//由dp[n][m][0]->dp[n+1][m][0] 45 return 0; 46 }