poj1942

题目很水,但是不好过,花了好几个小时,最开始用朴素的枚举,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;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值