• Singapore NOI2012 MODSUM题解


    MODSUM(modsum.pas/c/cpp)

    【问题描述】

    在本题中,你得到了一个含有n个参数的函数f

    f(x1,…,x2)=(((x1+x2+…+xn)4+2*(x1+x2+…+xn)2) mod 5)+1

    为了避免出现争议,函数f只接受整数参数。你的任务是计算f的所有函数值的和,每个参数xi是从vi到wi之间的整数。也就是说,你需要计算

    例如,如果n=3,v1=2,w1=3.v2=10,w2=12,v3=17,w3=17,那么这个结果应该是19。因为f(2,10,17) = 4, f(2,11,17) = 1, f(2,12,17) = 4, f(3,10,17) = 1,f(3,11,17) = 4 and f(3,12,17) = 5。

    重要提示:你可以认为这个结果是总小于1,000,000的。

    【输入】

    你的程序必须从标准输入中读取数据。输入包含一个数n(1≤n≤1000),跟在n后面有n对数,vi和wi,你可以认为vi≤wi,vi和wi都是0~100之间的整数。比如上面这个例子,输入是:

    3 2 3 10 12 17 17

    【输出】

    你的程序必须从标准输出中输出。输出计算出的和。比如上面这个例子,输出则是:

    19

    【数据范围】

    你的程序将会被5套测试数据测试:

    1. (5分)n≤6;

    2. (5分)n≤20;

    3. (5分)n≤100;

    4. (5分)n≤200;

    5. (5分)n≤1000.

    【题目分析】

    看到本题,我们第一个反应到的算法应该就是搜索算法,深度优先搜索解答树,即枚举每个数从vi到wi的值,逐个计算函数值,最后累加。但是分析一下极端情况我们会发现,枚举量会达到1011000,这个枚举量是难以接受的,但是题目中给了我们一个重要信息——最后的值不会超过1,000,000,每一个函数值最小为1,最大为5,和不超过1,000,000,也就代表着最多枚举量不会超过1,000,000。这样我们使用DFS搜索解答树完全是可行的。但是由于循环的层数不固定,所以我们通过递归的方式来变相的达到循环的效果。

    比如题目中所给的例子的解答树是:

    但是从DFS可以看出,它计算了很多重复的值,浪费了很多的时间。那么我们可以考虑采用BFS的思想,逐层进行DP的方式进行处理。

    在此之前,我们先计算出一个表格,用来计算当Σxi%5分别等于 0,1,2,3,4时f(x1,…,xn)的取值,方便后面的计算。

    Σxi%5

    f(x1,…,xn)

    0

    1

    1

    4

    2

    5

    3

    5

    4

    4

    那么,我们设f[i][y]表示前i个数里面,Σxi%5=y的种类数。那么状态转移方程为:

    (其中,c[i][z]表示第i个数所在的范围vi到wi之间除以5余z的数的数量。边界f[0][0]=1)

    例如:f[4][0]=f[3][0]*c[4][0]+f[3][1]*c[4][4]+f[3][2]*c[4][3]+f[3][3]*c[4][2]+f[3][4]*c[4][1]。

    样例分析:前四个数的和除以5余0的可能有:前三个数的和余0的数再加上第四个数的范围内余0的数,最终和余数仍为0(种类用乘法);前三个数的和余1的数再加上第四个数的范围内余4的数,最终和余数仍为0(种类用乘法)…以此类推。

    而最后,我们把f[n][0]~f[n][4]中的数分别和上表的数相乘,再相加即为最终答案。

    【程序源代码】

    1. 递归计算代码:

     
    1. //modsum.cpp by JerryXie   
    2.   
    3. #include<cstdio>   
    4. using namespace std;   
    5. struct para //定义结构体用来表示变量   
    6. {   
    7.   int v,w;   
    8. };   
    9.   
    10. int n,xx[1001],finalans=0;   
    11. para p[1001];   
    12. int f() //计算函数值    
    13. {   
    14.     int i,sum=0,ans=0,temp;   
    15.     for(i=1;i<=n;i++)   
    16.       sum+=xx[i];   
    17.     sum%=5;   
    18.     temp=sum;   
    19.     sum=sum*sum*sum*sum;   
    20.     sum%=5;   
    21.     ans+=sum;   
    22.     sum=temp;   
    23.     sum=sum*sum*2;   
    24.     sum%=5;   
    25.     ans+=sum;   
    26.     ans%=5;   
    27.     ans++;   
    28.     return ans;   
    29. }   
    30. void work(int xi) //递归取数    
    31. {   
    32.      int i;   
    33.      for(i=p[xi].v;i<=p[xi].w;i++)   
    34.      {   
    35.        xx[xi]=i; //循环选定每一层的数    
    36.        if(xi==n) //如果已经选完    
    37.          finalans+=f(); //计算函数值    
    38.        else //没选完    
    39.          work(xi+1); //继续递归下一层    
    40.      }   
    41. }   
    42. int main() //主函数    
    43. {   
    44.     int i;   
    45.     scanf("%d",&n);   
    46.     for(i=1;i<=n;i++)   
    47.       scanf("%d%d",&p[i].v,&p[i].w);   
    48.     work(1); //调用递归函数计算    
    49.     printf("%d",finalans);   
    50.     return 0;   
    51. }   

    2. 动态规划代码:

     
    1. //modsum.cpp by JerryXie   
    2.   
    3. #include<cstdio>   
    4. using namespace std;   
    5. struct para //定义结构体用来表示变量    
    6. {   
    7.   int v,w;   
    8. };   
    9.   
    10. para p[1001];   
    11. int f[1001][10],a[10],c[1001][10];   
    12. void create() //初始化    
    13. {   
    14.     a[0]=1;a[1]=4;a[2]=5;a[3]=5;a[4]=4; //初始化计算的参数和与函数值的关系    
    15.     f[0][0]=1; //初始化DP边界    
    16.     return;    
    17. }   
    18. int main()   
    19. {   
    20.     int i,j,k,n,temp,ans=0;   
    21.     scanf("%d",&n);   
    22.     create();   
    23.     for(i=1;i<=n;i++)   
    24.     {   
    25.       scanf("%d%d",&p[i].v,&p[i].w); //读数    
    26.       for(j=p[i].v;j<=p[i].w;j++) //计算第i个数的取值范围中除以5与temp的数量    
    27.       {   
    28.         temp=j%5;   
    29.         c[i][temp]++;   
    30.       }   
    31.     }   
    32.     for(i=1;i<=n;i++) //开始DP    
    33.     {   
    34.       for(j=0;j<=4;j++)   
    35.       {   
    36.         for(k=0;k<=4;k++)   
    37.         {   
    38.           f[i][j]+=f[i-1][(j+5-k)%5]*c[i][k];   
    39.         }   
    40.       }   
    41.     }   
    42.     for(i=0;i<=4;i++) //计算最终结果    
    43.       ans+=f[n][i]*a[i];   
    44.     printf("%d",ans);   
    45.     return 0;   
    46. }   

    欢迎鄙视。

  • 相关阅读:
    mybatisplus学习
    JSON数组读取和保存
    XML文件读写示例
    JSON代码示例
    Delphi接口示例代码
    获得wav语音文件时长
    javaweb注册页面学习之路(三)
    javaweb注册页面学习之路(二)
    javaweb注册页面学习之路(一)
    Django 目录
  • 原文地址:https://www.cnblogs.com/jerryxie/p/4780693.html
Copyright © 2020-2023  润新知