题目很水,但是不好过,花了好几个小时,最开始用朴素的枚举,TLE了,后来优化深搜,也TLE了,最后用了dp+动态滚动数组时间复杂度在O(N*M)本以为没有问题了吧,谁知道又TLE了。郁闷哪!,先简单的说明一下dp吧!虽然超时了(因为数据量可以到达65535*65535那么即使是O(N*M)的时间复杂度也必定超时):
令dp[i][j]=表示从第i行第j列到达终点时的不同路径数。那么动态转移方程如下:
dp[i][j]=dp[i-1][j]+dp[i][j+1]
原理很简单:因为只有两种选择方式:不是UP就是RIGHT,所以dp[i][j]即为向上走的不同路径数+向右走的不同路径数。为了节省空间复杂度,这里用到了动态滚动数组,第一次真正的使用滚动数组,主要是想尝试一下该方法(虽然这题本没有空间上过多的限制,主要是时间卡)。滚动数组使用的关键在于清晰动态转方程,以及求解过程。最好能够在纸上模拟该过程,防止发生错误。令外,i%2与(i+1)%2应该是非常有用的(在滚动数组中)。
下面进入主题:排列组合求解(O(MIN(N,M))时间复杂度)
对于给定的n,m,要从左下方点到达右上方点则必须要向左走m次,向上走n次,那么不同的路径即 right 与 up 的不同组合情况了。问题即转化成如下模型:
(N+M)Cn 或 (N+M)Cm (两者相等),而(N+M)CN=(N+M)!/N!*M!,那么现在的核心就是如何求解该式子了。可以将该式子转化为:
(N+M)CN=N+M + N+M-1 + ……+ M+1 / N + N-1 + ……+ 1
这样我们就可依次循环解决问题了如下:
t=Min(n,m);
total=n+m;
double result=1.0;
while(t>0){
result*=(double(total)/double(t));
total--,t--;
}
另外要注意需要先将unsigned转化为double,在将double结果四舍五入后转化为unsigned。转化的方法有两种(详细见代码)
代码如下: 144K+0MS
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <iomanip>
#define Min(a,b) (a)<(b)?(a):(b)
using namespace std;
unsigned n,m,t,total;
int main(){
while(scanf("%u%u",&n,&m)){
if(n==0 && m==0)
break;
/*if(n==0 || m==0){
printf("0\n");
continue;
}*/
t=Min(n,m);
total=n+m;
double result=1.0;
while(t>0){
result*=(double(total)/double(t));
total--,t--;
}
//unsigned temp=result+0.5;
printf("%u\n",unsigned(result+0.5));
//cout << fixed << setprecision(0) << result << endl;
}
return 0;
}
下面贴出我朴素枚举,优化深搜,dp+滚动数组的代码(供参考):
朴素枚举:
#include <stdio.h>
#include <stdlib.h>
int n,m;
int Sum;
void dfs(int x,int y){
if(x<1 || y>m)
return ;
if(x==1 && y==m){
Sum++;
return ;
}
dfs(x,y+1);
dfs(x-1,y);
}
int main(){
while(scanf("%d%d",&n,&m)){
if(n==0 && m==0)
break;
if(n==0 || m==0){
printf("0\n");
continue;
}
n+=1,m+=1;
Sum=0;
dfs(n,1);
printf("%d\n",Sum);
}
return 0;
}
优化深搜:
#include <stdio.h>
#include <stdlib.h>
int n,m;
int Sum;
void dfs(int x,int y){
if(x==1 && y==m){
Sum++;
return ;
}
if(y+1<=m)
dfs(x,y+1);
if(x-1>=1)
dfs(x-1,y);
}
int main(){
while(scanf("%d%d",&n,&m)){
if(n==0 && m==0)
break;
if(n==0 || m==0){
printf("0\n");
continue;
}
n+=1,m+=1;
Sum=0;
dfs(n,1);
printf("%d\n",Sum);
}
return 0;
}
dp+动态滚动数组:
#include <stdio.h>
#include <stdlib.h>
int n,m;
int main(){
while(scanf("%d%d",&n,&m)){
if(n==0 && m==0)
break;
if(n==0 || m==0){
printf("0\n");
continue;
}
n+=1,m+=1;
int **dp=new int *[2];
dp[1]=new int[m+1];
dp[0]=new int[m+1];
int i,j;
dp[0][0]=dp[1][0]=0;
for(i=1;i<=m;i++)
dp[1][i]=1;
for(i=2;i<=n;i++)
for(j=1;j<=m;j++)
dp[i%2][j]=dp[i%2][j-1]+dp[(i+1)%2][j];
printf("%d\n",dp[n%2][m]);
for(int i=0;i<2;i++)
delete []dp[i];
delete []dp;
}
return 0;
}