zoukankan      html  css  js  c++  java
  • UVA 1393 Highways,UVA 12075 Counting Triangles —— (组合数,dp)

      先看第一题,有n*m个点,求在这些点中,有多少条直线,经过了至少两点,且不是水平的也不是竖直的。

      分析:由于对称性,我们只要求一个方向的线即可。该题分成两个过程,第一个过程是求出n*m的矩形中,dp[i][j]代表在这个矩形中终点是到(i,j)这个点的满足题意的直线条数,那么,用dp的话就可以得出递推关系:由长和宽分别小1的左右两个矩形中满足题意的线的条数减去他们共有的矩形中满足的线的条数(容斥减去重复部分),之后还要判断从最左上角的点(1,1)到(i,j)是否可以组成一条线,这个条件是gcd(i,j)是否等于1。

      之后第二个过程就是递推答案了,设ans[i][j]表示在这个矩形中满足题意的条数,那么同样的,可以由上面的容斥来递推,同时,还要加上这个矩形内到(i,j)这个点满足的条数,另外还要减去一半规模大小的到这个点的线的条数,因为如果(i,j)为(6,8),那么一半规模下,(1,1)到(3,4)这个点的线和到(6,8)这条线是重复的。

      这样就做完了题目(最后不要忘了乘以2)。具体见代码:

     1 #include <stdio.h>
     2 #include <algorithm>
     3 #include <string.h>
     4 #include <iostream>
     5 #include <vector>
     6 #include <queue>
     7 using namespace std;
     8 typedef long long ll;
     9 const int N = 300+5;
    10 
    11 int dp[N][N];
    12 int ans[N][N];
    13 
    14 int gcd(int a,int b) {return a%b?gcd(b,a%b):b;}
    15 
    16 void init()
    17 {
    18     for(int i=1;i<N;i++)
    19     {
    20         for(int j=1;j<N;j++)
    21         {
    22             dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + (gcd(i,j)==1);
    23         }
    24     }
    25     for(int i=1;i<N;i++)
    26     {
    27         for(int j=1;j<N;j++)
    28         {
    29             ans[i][j] = ans[i-1][j] + ans[i][j-1] - ans[i-1][j-1] + dp[i][j] - dp[i>>1][j>>1];
    30         }
    31     }
    32 }
    33 
    34 int main()
    35 {
    36     init();
    37     int n,m;
    38     while(scanf("%d%d",&n,&m)==2)
    39     {
    40         if(n==0 && m==0) break;
    41         printf("%d
    ",2*ans[n-1][m-1]);
    42     }
    43 }

      做出了这题,第二题就是类似的了。先在所有的点中枚举出选3个点的可能性,然后,减去一条水平或者竖直线上重复的,再减去同在一条斜线上重复的即可。相当类似,具体见代码吧:

     1 #include <stdio.h>
     2 #include <algorithm>
     3 #include <string.h>
     4 #include <iostream>
     5 #include <vector>
     6 #include <queue>
     7 using namespace std;
     8 typedef long long ll;
     9 const int N = 1000+5;
    10 
    11 ll dp[N][N];
    12 ll ans[N][N];
    13 
    14 int gcd(int a,int b) {return a%b?gcd(b,a%b):b;}
    15 
    16 void init()
    17 {
    18     for(int i=1;i<N;i++)
    19     {
    20         for(int j=1;j<N;j++)
    21         {
    22             dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + (ll)(gcd(i,j)-1);
    23         }
    24     }
    25     for(int i=1;i<N;i++)
    26     {
    27         for(int j=1;j<N;j++)
    28         {
    29             ans[i][j] = ans[i-1][j] + ans[i][j-1] - ans[i-1][j-1] + dp[i][j];
    30         }
    31     }
    32 }
    33 
    34 ll C(ll x) {return x*(x-1)*(x-2)/6;}
    35 
    36 int main()
    37 {
    38     init();
    39     int n,m;
    40     int cnt = 1;
    41     while(scanf("%d%d",&n,&m)==2)
    42     {
    43         if(n==0 && m==0) break;
    44         ll Ans = C((n+1)*(m+1)) - (n+1)*C(m+1) - (m+1)*C(n+1);
    45         Ans -= 2*ans[n][m];
    46         cout<<"Case "<<cnt++<<": "<<Ans<<endl;
    47     }
    48 }

      

      但是,这两题都要注意的地方是,递推dp时三个矩形都是在右下角的(因为向下或者向右平移一个单位的话条数是不变的),这样递推起来的话只要再考虑从(1,1)这个点到(i,j)这个点的情况即可;而递推ans的时候,矩形是偏左上方的,那么,只要再加上整个大矩形内到(i,j)这个点的情况即可。当然,纯属个人理解。

  • 相关阅读:
    JS—图片压缩上传(单张)
    vue 使用jssdk分享
    微信JS-SDK选择图片遇到的坑
    手把手教你实现一个微信自动回复机器人
    SSH实现远程控制
    使用Apache服务部署静态网站
    Rhel7安装及网卡、yum、vmtools配置和修改主机名
    基础工具之消息队列、线程池、缓冲区抽象、事件循环和日志实现
    I/O多路复用方案
    Java按字节截取字符串(GBK编码、UTF-8编码实现)
  • 原文地址:https://www.cnblogs.com/zzyDS/p/5664689.html
Copyright © 2011-2022 走看看