上学路线

 时间限制: 1 s空间限制: 64000 KB

题目描述 Description

因为是学生,所以显然小A每天都要上学。小A所在的城市的道路构成了一个n*m的网格,每条道路都是可以单向通行的。小A家在点(1,1),学校在点(n,m)。在不移出边界的情况下,小A可以从点(x,y)移动到点(x+1,y)或(x,y+1)。为了追求新鲜感,小A经常走不同的道路去上学。有一天,小A突发奇想:到底他可以有多少种不同的上学路线呢?

输入描述 Input Description

两个正整数n,m,意义如前所述。

输出描述 Output Description

一个正整数,小A的上学路线数量。

样例输入 Sample Input

2 3

样例输出 Sample Output

3

数据范围及提示 Data Size & Hint

对于30%的数据,n,m<=100;

对于100%的数据,n+m<=40000.

是不是感覺這道題很水啊(才怪呢,有沒有看數據範圍)。

搜索代碼(10分):

#include<cstdio>
int n,m,ans;
void dfs(int x,int y){
if(x==n&&y==m){++ans;return;}
if(y<m) dfs(x,y+1);
if(x<n) dfs(x+1,y);
}
int main(){
scanf("%d%d",&n,&m);
dfs(1,1);
printf("%d
",ans);
return 0;
}

對的,這是個DP題。

數據:

1  1  1   1   1    1    1     1     1     1 
1  2  3   4   5    6    7     8     9    10 
1  3  6  10  15   21   28    36    45    55 
1  4 10  20  35   56   84   120   165   220 
1  5 15  35  70  126  210   330   495   715 
1  6 21  56 126  252  462   792  1287  2002 
1  7 28  84 210  462  924  1716  3003  5005 
1  8 36 120 330  792 1716  3432  6435 11440 
1  9 45 165 495 1287 3003  6435 12870 24310 
1 10 55 220 715 2002 5005 11440 24310 48620

 

相信聰明的,你,一定,看出了DP方程。

f[i][j]=f[i-1][j]+f[i][j-1];

不要問我為什麼,DP是玄學。

代碼實現(10):

#include<cstdio>
int n,m,f[3000][3000];
int main(){
scanf("%d%d",&n,&m);f[0][1]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
f[i][j]=f[i-1][j]+f[i][j-1];
printf("%d
",f[n][m]);
return 0;
}

為什麼還是10分,啊啊啊。。。(省略很多)

我們來看看問題,

Wa:爆int了。

Re:開的數組不夠大了。

對於Re,因為根據DP方程看,我們只需要一層的數據就夠了。(f[j]+=f[j-1];)

對於Wa,我們只需要使用高精就行了。

還有一個Tle,恩,可能是哪裡卡住了吧。

恩,讓我平靜一下。

代碼實現(不要管這個):我正寫著寫著,旁邊那個同學突然說,****,這個題是組合數,你看看題解,沒有用DP的。

啊,我的上帝原諒我,我點擊題解,快速瀏覽了一下,好像真的,而且一個大佬還說最高二十萬位的樣子。

。。。

慫它嗎?

舉(5,5)為例,按f[5][5]=f[4][5]+f[5][4];f[4][5]=f[3][5]+f[4][4],f[5][4]=f[4][4]+f[5][3];f[3][5]=f[2][5]+f[3][4],2*f[4][4]=2*f[4][3]+2*f[3][4];f[5][3]=f[5][2]+f[4][3]......

即f[5][5]=20*f[2][1]+10*f[3][1]+4*f[4][1]+f[5][1]+20*f[1][2]+10*f[1][3]+4*f[1][4]+1*f[1][5]=20+10+4+1+20+10+4+1;

額,你看出什麼來了嗎?我沒看出來。。。

那麼,

1   1   1    1     1       1       1        1           1         1
1   2   3    4     5       6       7        8           9       10
1   3   6   10   15     21     28      36        45       55
1   4 10   20   35     56     84     120     165     220
1   5 15   35   70   126   210     330     495     715
1   6 21   56 126   252   462     792   1287   2002
1   7 28   84 210   462   924   1716   3003   5005
1   8 36 120 330   792 1716   3432   6435 11440
1   9 45 165 495 1287 3003   6435 12870 24310 
1 10 55 220 715 2002 5005 11440 24310 48620

f[2][3]=f[2][1]+f[1][2]+f[1][3]=1+1+1;

f[3][3]=f[3][1]+2*f[2][1]+2f[1][2]+f[1][3]=1+2+2+1;

f[4][3]=f[4][1]+2*f[3][1]+3*f[2][1]+3*f[1][2]+f[1][3]=1+2+3+3+1;

f[5][3]=......
心路歷程

好吧,C(n,m)=m!/n!(m-n)!。

要處理一下n、m。

if(n<m){a=m,m=n,n=a;}
n--;m+=n-1;

也就是組合數加高精。

代碼實現:

 1 #include<cstdio>
 2 using namespace std;
 3 int n,m,a,ans[13000]={1,1},bz[13000];
 4 int s[40000],g[40000];
 5 int v[40000];
 6 void zzs(int x,bool y){
 7     for(int j=1;j<=s[0]&&x!=1;j++)
 8     while(x%s[j]==0){
 9         if(y) ++g[j];
10         else --g[j];
11         x/=s[j];
12     }
13 }
14 void gjc(int x){
15     g[x]--;
16     for(int i=1;i<=ans[0];i++){
17         ans[i]=ans[i]*s[x]+bz[i];bz[i]=0;
18         if(ans[i]>9){
19             bz[i+1]=ans[i]/10;
20             ans[i]%=10;
21             if(i==ans[0]) ++ans[0];
22         }
23     }
24 }
25 int main(){
26     for(int i=2;i<=40000;i++)
27     if(!v[i]){
28         a=2*i;s[++s[0]]=i;
29         while(a<=40000){v[a]=1;a+=i;}
30     }
31     scanf("%d%d",&n,&m);
32     if(n<m){a=m,m=n,n=a;}
33     n--;m+=n-1;
34     for(int i=n+1;i<=m;i++) zzs(i,1);
35     for(int i=1;i<=m-n;i++) zzs(i,0);
36     for(int i=1;i<=s[0];i++)
37     while(g[i]) gjc(i);
38     for(int i=ans[0];i>0;i--) printf("%d",ans[i]);
39     printf("
");
40     return 0;
41 }

最高12039位,呵呵。

原文地址:https://www.cnblogs.com/J-william/p/6046719.html