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. 递归计算代码:
- //modsum.cpp by JerryXie
- #include<cstdio>
- using namespace std;
- struct para //定义结构体用来表示变量
- {
- int v,w;
- };
- int n,xx[1001],finalans=0;
- para p[1001];
- int f() //计算函数值
- {
- int i,sum=0,ans=0,temp;
- for(i=1;i<=n;i++)
- sum+=xx[i];
- sum%=5;
- temp=sum;
- sum=sum*sum*sum*sum;
- sum%=5;
- ans+=sum;
- sum=temp;
- sum=sum*sum*2;
- sum%=5;
- ans+=sum;
- ans%=5;
- ans++;
- return ans;
- }
- void work(int xi) //递归取数
- {
- int i;
- for(i=p[xi].v;i<=p[xi].w;i++)
- {
- xx[xi]=i; //循环选定每一层的数
- if(xi==n) //如果已经选完
- finalans+=f(); //计算函数值
- else //没选完
- work(xi+1); //继续递归下一层
- }
- }
- int main() //主函数
- {
- int i;
- scanf("%d",&n);
- for(i=1;i<=n;i++)
- scanf("%d%d",&p[i].v,&p[i].w);
- work(1); //调用递归函数计算
- printf("%d",finalans);
- return 0;
- }
2. 动态规划代码:
- //modsum.cpp by JerryXie
- #include<cstdio>
- using namespace std;
- struct para //定义结构体用来表示变量
- {
- int v,w;
- };
- para p[1001];
- int f[1001][10],a[10],c[1001][10];
- void create() //初始化
- {
- a[0]=1;a[1]=4;a[2]=5;a[3]=5;a[4]=4; //初始化计算的参数和与函数值的关系
- f[0][0]=1; //初始化DP边界
- return;
- }
- int main()
- {
- int i,j,k,n,temp,ans=0;
- scanf("%d",&n);
- create();
- for(i=1;i<=n;i++)
- {
- scanf("%d%d",&p[i].v,&p[i].w); //读数
- for(j=p[i].v;j<=p[i].w;j++) //计算第i个数的取值范围中除以5与temp的数量
- {
- temp=j%5;
- c[i][temp]++;
- }
- }
- for(i=1;i<=n;i++) //开始DP
- {
- for(j=0;j<=4;j++)
- {
- for(k=0;k<=4;k++)
- {
- f[i][j]+=f[i-1][(j+5-k)%5]*c[i][k];
- }
- }
- }
- for(i=0;i<=4;i++) //计算最终结果
- ans+=f[n][i]*a[i];
- printf("%d",ans);
- return 0;
- }
欢迎鄙视。