51nod 1118 机器人走方格:
思路:这是一道简单题,很容易就看出用动态规划扫一遍就可以得到结果,
时间复杂度O(m*n)。运算量1000*1000 = 1000000,很明显不会超时。
递推式子:dp[i][j] = dp[i-1][j] + dp[i][j-1]。 dp[i][j]表示当规格为i*j (m = i && n = j) 时本题的结果。
直接上代码:
#include <stdio.h> #include <string.h> #define rep(i,o,u) for(int i = o;i <= u; i++) long long a[1002][1002]; int main(){ int n,m; scanf("%d%d",&n,&m); memset(a,0,sizeof(a)); a[1][1] = 1; rep(i,1,n){ rep(j,1,m){ a[i][j] = (a[i][j] + a[i-1][j]+a[i][j-1])%1000000007; } } printf("%d ", a[n][m]); return 0; }
51nod 1119 机器人走方格 V2:
分析:这题和上题的不同之处在于数据量变大了。如果还按上题的动态规划。时间复杂度O(m*n)。
运算量将变成10^6*10^6 = 10^12,很明显会超时。
这时我们需要换一条思路。
一般没有思路的题我都会去找规律,如果找不出规律一般都是需要用到特殊的数据结构或者自己能力不够。
这题也不例外,我开始了找规律,我发现1118,也就是上面那个题可以得到这个题的部分数据。
好吧我们先做个试验把,我修改了一下1118的代码,输出了 m = [2,6] & n = [2,6]范围内的数据如下:
然后惊讶的发现这不就是杨辉三角吗?
想起杨辉三角的定义就是左边和上面两个值之和。
我们知道杨辉三角有一个组合数的性质。
然后我们观察这些数据可以发现dp[i][j] = C(m+n-2,m-1) = C(m+n-2,n-1)。
然后这题就迎刃而解了,转化成了组合数问题。
可是求组合数也有一个问题?因为数字过大所以需要取模,我们知道负数取模需要用到逆元。
如果不知道逆元,就看一下这个:点击这里转到博客。
然后就可以上代码了:
#include <bitsstdc++.h> using namespace std; typedef long long ll; #define Mod 1000000007 //求逆元模板 begin ll gcd(ll a,ll b,ll &x,ll &y){ if (b==0){ x=1,y=0; return a; } ll q=gcd(b,a%b,y,x); y-=a/b*x; return q; } ll ni(ll m,ll n) { ll x = 0,y = 0; gcd(n,m,x,y); if(y > 0) return y; else return n+y; } //求逆元模板 end ll C(ll a,ll b){ ll num1 = 1,num2 = 1; //求组合数的分子 for(int i = a-b+1;i <= a; i++){ num1 = (num1*i)%Mod; } //求组合数的分母 for(int i = 1;i <= b; i++){ num2 = (num2*i)%Mod; } //求 (分子num1/分母num2)%Mod 可以转化为 (num1*num2的逆元)%Mod //如果想知道逆元的原理可以看一下我写的另一篇博客 http://www.cnblogs.com/zhangjiuding/p/7546158.html cout << (num1*ni(num2,Mod))%Mod << endl; } int main() { int m,n; cin >> m >> n; C(m+n-2,n-1); return 0; }