noip1997 街道 (动态规划,方格左下角到右上角路径数)

A1110. 街道
时间限制: 1.0s   内存限制: 256.0MB  
总提交次数: 547   AC次数: 118   平均分: 55.47
将本题分享到:
       
   
试题来源
  NOIP1997 普及组
问题描述
  设有一个N*M(l≤ N≤50, l≤ M≤ 50)的街道。n和m表示横竖街道数。
  规定行人从A(1,1)出发,在街道上只能向东或北方向行走。
  N=3,M=3的街道图,从A出发到达B共有6条可供行走的路。
  若在N*M的街道中,设置一个矩形障碍区域(包括围住该区域的街道和点)不让行人通行。
  此矩形障碍区域用2对顶点坐标给出,前图中的2对顶点坐标为:(2,2),(8,4),此时从 A出发到达B的路径仅有两条。

  程序要求:

  任务一:给出N,M后,求出所有从A出发到达B的路径的条数。

  任务二:给出N,M,同时再给出此街道中的矩形障碍区域的2对顶点坐标(X1,y1), (X2,Y2),然后求出此种情况下所有从A出发到达B的路径的条数。
  如果答案太大,输出最后20位。
输入格式
  第一行两个数n和m。
  第二行为X1,Y1,X2,Y2.如果是任务一,则第二行为4个0.
输出格式
  输出走路方案数。
样例输入
3 3
0 0 0 0
样例输出
6
样例输入
50 50
2 2 49 49
样例输出
2
数据规模和约定
  1<=N,M<=50

解析:求解从矩形左下角到右上角的方案数。
  ①我们发现,初始在(1,1)的位置,然后第一步走出的位置可以为:(1,2)、(2,1),其坐标之和为3
   以此类推,第二步到达的点坐标之和为4,第三步到达的点坐标之和为5。。。
   
   我们用f[i][j]表示从起点到达(i,j)的方案数,则有:f[i][j]=f[i-1][j]+f[i][j-1];
   flag[i][j]表示(i,j)点能否通过。
   于是得到递推式:
   for(k=3;k<=n+m;k++)
    for(i=1;i<=n;i++)
     {  
      j=k-i;
      f[i][j]=f[i-1][j]+f[i][j-1];
     }
   当然,还要保证上述的点都是有效的,能通过的。
 ②本题的第二个难点,结果可能很大,保留最后20位即可,但是最大的unsigned long long 也不过刚好20位,显然是存不下的。这个时候就只有用高精度了。
  但是只要保存最后20位,所以直接假设ans是一个20位的整数,前10用d1来保存,后10位用d2来保存,这样只要在想家的过程中稍作处理即可,而不用真的去写一个高精度。
代码:
#include<cstdio>
#include<algorithm>
#define maxn 50
#define len 10000000000
using std::swap;
struct tnode{long long d1,d2;}f[maxn+10][maxn+10];
int n,m;
int flag[maxn+10][maxn+10];
void redirect()
{
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
}
void chuli()
{
  int i,j,k;
  long long s;
  f[1][1].d1=1,f[1][1].d2=0;
  for(k=3;k<=n+m;k++)
    for(i=1;i<=n;i++)
      if(k-i>=1 && k-i<=m && !flag[i][k-i])
        {
          j=k-i;
          if(j-1>=1)f[i][j]=f[i][j-1];
          if(i-1>=1)
            {
              s=f[i-1][j].d1+f[i][j].d1;
              f[i][j].d1=s%len;
              f[i][j].d2=(f[i-1][j].d2+f[i][j].d2+s/len)%len;
            }
        }
   if(f[n][m].d2==0)printf("%I64d\n",f[n][m].d1);
   else printf("%I64d%010I64d\n",f[n][m].d2,f[n][m].d1);   
}
void work()
{
  int x1,x2,y1,y2,i,j,k;
  scanf("%d%d",&n,&m);
  scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
  if(x1>x2)swap(x1,x2);
  if(y1>y2)swap(y1,y2);
  for(i=x1;i<=x2;i++)
    for(j=y1;j<=y2;j++)
      flag[i][j]=1;
  chuli();    
}
int main()
{
  //redirect();
  work();
  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值