题目描述 Description
给定一个信封,最多只允许粘贴N张邮票,计算在给定K(N+K≤40)种邮票的情况下(假定所有的邮票数量都足够),如何设计邮票的面值,能得到最大值MAX,使在1~MAX之间的每一个邮资值都能得到。
例如,N=3,K=2,如果面值分别为1分、4分,则在1分~6分之间的每一个邮资值都能得到(当然还有8分、9分和12分);如果面值分别为1分、3分,则在1分~7分之间的每一个邮资值都能得到。可以验证当N=3,K=2时,7分就是可以得到的连续的邮资最大值,所以MAX=7,面值分别为1分、3分。
N和K
每种邮票的面值,连续最大能到的面值数。数据保证答案唯一。
3 2
1 3
MAX=7
很好的一道题!实际上这个题是个DFS搜索题,通过DFS搜索下一个可以出现的邮票面值,但是得到邮票面值后需要求出能凑出的最大邮资,如果光用枚举,虽然数据范围不大,但是算上去也是个很大的常数了,加上DFS搜索次数太多,这样做会超时,比较好的办法就是用完全背包f[v]表示凑出邮资v最少要多少张邮票,每次得到一个邮票面值,就记录下它,用“我为人人”型动规更新f数组,然后从小到大搜索一遍下一种邮票面值,直到所有邮票面值都枚举完,更新最大邮资
另外需要注意的是搜索下一种邮票面值时,也不能乱枚举,这样会让搜索树中每个节点的儿子太多,搜索树就会太大,一样会超时,那么枚举就得有上下界。
下界的求法是,枚举的下一种邮票面值要保证比上一种大,这样邮票面值序列就会是单调递增的,很明显,下界是之前求出的邮票面值中的最后一种面值x+1,简单证明:
这样一来此题思路就清晰了,下面是代码
#include <stdio.h> #include <string.h> #include <stdlib.h> #define MAXV 1000 #define MAXN 40 #define INF 1000000 int f[MAXV],stamp[MAXN],nowSol[MAXN],maxValue; //f[v]=凑出面额v至少要多少张邮票,stamp是保存面值的数组,nowSol是当前DFS搜出的邮票面值,max=当前最大总面值 int n,k; void dfs(int cnt,int sum) //已经有了cnt种邮票(当前正在尝试第cnt+1种邮票),而且 { int copy[MAXV]; //拷贝数组f用 if(cnt==k) //所有邮票的面值都枚举完了 { if(sum>maxValue) //如果当前的最大总面值超过之前的答案,那么这是新的最优解 { maxValue=sum; for(int i=1;i<=cnt;i++) stamp[i]=nowSol[i]; } return; } for(int i=0;i<MAXV;i++) copy[i]=f[i]; //后面的操作会修改f数组,所以先将f数组拷贝保存起来,回溯后再恢复回去 //完全背包求出使用该种邮票后可以获得的面值 for(int i=nowSol[cnt]+1;i<=sum;i++) //枚举下一个面额 { for(int j=0;j<MAXV-i;j++) if(f[j]+1<f[j+i]) f[j+i]=f[j]+1; int nowMaxValue; //当前能凑出的最大邮资 for(nowMaxValue=sum+1;f[nowMaxValue]<=n;nowMaxValue++); //寻找能得到的最大面值 nowSol[cnt+1]=i; //加入新的邮票 dfs(cnt+1,nowMaxValue); for(int j=0;j<MAXV;j++) f[j]=copy[j]; //把f数组拷贝回来 } } int main() { int i; scanf("%d%d",&n,&k); nowSol[1]=1; //第一张邮票的面值一定为1(废话,不用1的话,一种邮票凑不出连续的邮资) for(i=1;i<=n;i++) f[i]=i; //DP边界1:f[i]=i,i<=n,因为这时邮资小,只用i张1元邮票就能凑起来 for(;i<MAXV;i++) f[i]=INF; //DP边界2:f[i]=+∞,因为这时邮资大了,邮票个数有限,光用1元邮票凑不齐,需要后面增加新面值以后,通过完全背包动规更新f数组 dfs(1,n); for(i=1;i<=k;i++) printf("%d ",stamp[i]); printf("\nMAX=%d\n",maxValue); system("pause"); return 0; }
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”。今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
主件
附件
电脑
打印机,扫描仪
书柜
图书
书桌
台灯,文具
工作椅
无
如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有0个、1个或2个附件。附件不再有从属于自己的附件。金明想买的东西很多,肯定会超过妈妈限定的N元。于是,他把每件物品规定了一个重要度,分为5等:用整数1~5表示,第5等最重要。他还从因特网上查到了每件物品的价格(都是10元的整数倍)。他希望在不超过N元(可以等于N元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第j件物品的价格为v[j],重要度为w[j],共选中了k件物品,编号依次为j1,j2,……,jk,则所求的总和为:
v[j1]*w[j1]+v[j2]*w[j2]+…+v[jk]*w[jk]。(其中*为乘号)
请你帮助金明设计一个满足要求的购物单。
第1行,为两个正整数,用一个空格隔开:
N m
(其中N(<32000)表示总钱数,m(<60)为希望购买物品的个数。)
从第2行到第m+1行,第j行给出了编号为j-1的物品的基本数据,每行有3个非负整数
v p q
(其中v表示该物品的价格(v<10000),p表示该物品的重要度(1~5),q表示该物品是主件还是附件。如果q=0,表示该物品为主件,如果q>0,表示该物品为附件,q是所属主件的编号)
只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<200000)
1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0
2200
这个题WA得太神奇了,数组开小了,fuck
具体说这就是个01背包的题,只不过决策时需要注意下,只对主件进行决策,要么不取主件,要么只取主件,要么取主件和1号附件,要么取主件和2号附件,要么主件和1、2号附件都取。
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXM 70 #define MAXN 30100 int w[MAXM][4]; //w[i][0]=i的价格,w[i][1]=i的1号附件价格,w[i][2]=i的2号附件价格 int v[MAXM][4]; //v[i][0]=i的价值,v[i][1]=i的1号附件价值,v[i][2]=i的2号附件价值 int f[MAXM][MAXN]; //f[v]=使用掉的钱为v时获得的最大价值 int max(int a,int b) { if(a>b) return a; return b; } int main() { int n,m,maxAns=-1; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int W,V,belong; scanf("%d%d%d",&W,&V,&belong); if(!belong) //是附件 { w[i][0]=W; v[i][0]=V; } else { if(!w[belong][1]) { v[belong][1]=V; //记录下主件有附件1 w[belong][1]=W; } else { v[belong][2]=V; //记录下主件有附件1 w[belong][2]=W; } } } for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) { if(j>=w[i][0]) { f[i][j]=max(f[i-1][j],f[i-1][j-w[i][0]]+v[i][0]*w[i][0]); //只要主件 if(j-w[i][0]-w[i][1]>=0) f[i][j]=max(f[i][j],f[i-1][j-w[i][0]-w[i][1]]+v[i][0]*w[i][0]+v[i][1]*w[i][1]); //主件+1号附件 if(j-w[i][0]-w[i][2]>=0) f[i][j]=max(f[i][j],f[i-1][j-w[i][0]-w[i][2]]+v[i][0]*w[i][0]+v[i][2]*w[i][2]); //主件+2号附件 if(j-w[i][0]-w[i][1]-w[i][2]>=0) f[i][j]=max(f[i][j],f[i-1][j-w[i][0]-w[i][1]-w[i][2]]+v[i][0]*w[i][0]+v[i][1]*w[i][1]+v[i][2]*w[i][2]); //主件+2号附件 } else f[i][j]=f[i-1][j]; } printf("%d\n",f[m][n]); system("pause"); return 0; }
设有N*N的方格图(N<=10,我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0。如下图所示(见样例):
某人从图的左上角的A 点出发,可以向下行走,也可以向右走,直到到达右下角的B点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。
此人从A点到B 点共走两次,试找出2条这样的路径,使得取得的数之和为最大。
输入的第一行为一个整数N(表示N*N的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的0表示输入结束。
只需输出一个整数,表示2条路径上取得的最大的和。
8
2 3 13
2 6 6
3 5 7
4 4 14
5 2 21
5 6 4
6 3 15
7 2 14
0 0 0
67
和之前的传纸条很类似,思路基本相同,照搬即可,这里不细说,但有个细节需要注意:这里两条路径是可以重合的,由于题目中有这个要求,所以动规时不能避开两个相同的点,如图
若两条路径有交叉(DP中两个点相同),那么就把重复算入的当前格子分数扣掉就行了。
#include <stdio.h> #include <string.h> #include <stdlib.h> #define MAXN 50 long long int f[MAXN][MAXN][MAXN][MAXN]; long long int n,map[MAXN][MAXN]; long long int max(long long int a,long long int b) { if(a>b) return a; return b; } int main() { scanf("%lld",&n); int x,y,num; while(1) { scanf("%d%d%d",&x,&y,&num); if(!x&&!y&&!num) break; map[x][y]=num; } for(int i=1;i<=n;i++) //第一条路径过点(i,j) for(int j=1;j<=n;j++) for(int k=1;k<=n;k++) for(int h=1;h<=n;h++) { f[i][j][k][h]=max(f[i][j][k][h],f[i-1][j][k-1][h]); f[i][j][k][h]=max(f[i][j][k][h],f[i-1][j][k][h-1]); f[i][j][k][h]=max(f[i][j][k][h],f[i][j-1][k-1][h]); f[i][j][k][h]=max(f[i][j][k][h],f[i][j-1][k][h-1]); f[i][j][k][h]+=map[i][j]+map[k][h]; if(i==k&&j==h) f[i][j][k][h]-=map[i][j]; } printf("%lld\n",map[n][n]+max(f[n-1][n][n][n-1],f[n][n-1][n-1][n])); return 0; }
设一个n个节点的二叉树tree的中序遍历为(l,2,3,…,n),其中数字1,2,3,…,n为节点编号。每个节点都有一个分数(均为正整数),记第j个节点的分数为di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下:
subtree的左子树的加分× subtree的右子树的加分+subtree的根的分数
若某个子树为主,规定其加分为1,叶子的加分就是叶节点本身的分数。不考虑它的空
子树。
试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树tree。要求输出;
(1)tree的最高加分
(2)tree的前序遍历
现在,请你帮助你的好朋友XZ设计一个程序,求得正确的答案。
第1行:一个整数n(n<=30),为节点个数。
第2行:n个用空格隔开的整数,为每个节点的分数(分数<=100)
第1行:一个整数,为最高加分(结果不会超过4,000,000,000)。
第2行:n个用空格隔开的整数,为该树的前序遍历。
5
5 7 1 2 10
145
3 1 2 4 5
n(n<=30)
分数<=100
乍一看这个题是数据结构题,但是因为一个中序遍历对应多个二叉树,这个题没办法建树,好像做不出来,但是细想NOIP怎么可能会考这么裸的二叉树呢?所以这个题是个区间型动规题,为了写起来方便,最好是用记忆化DFS来写,也不容易错。
可以用一个二元组(或者说区间左右端点)[L,R]表示当前递归层次的状态,dfs(L,R)=求区间[L,R]对应二叉树子树能获得的最大分数,为了能在线段区间中模拟树结构的递归深搜过程,可以进行下图的操作
动规的思路也很清晰,套用区间型动规的通用方程就行,f[L,R]=max{f[L,mid-1]*f[mid+1,R]+value[mid]},这里mid就是要取的子树根结点
#include <stdio.h> #include <string.h> #include <stdlib.h> #define MAXN 100 int f[MAXN][MAXN]; //f[i][j]=中序遍历序列区间[1,j]获得分数的最大值 int visit[MAXN][MAXN]; int root[MAXN][MAXN]; int max(int a,int b) { if(a>b) return a; return b; } int dp(int L,int R) // { if(L==R) return f[L][R]; if(L>R) return 1; if(visit[L][R]) return f[L][R]; visit[L][R]=1; int maxAns=-1; for(int mid=L;mid<=R;mid++) if(maxAns<(dp(L,mid-1)*dp(mid+1,R)+f[mid][mid])) { root[L][R]=mid; int Ltree=dp(L,mid-1); int Rtree=dp(mid+1,R); maxAns=(Ltree*Rtree+f[mid][mid]); } return f[L][R]=maxAns; } void firstPrint(int L,int R) { if(L>R) return; printf("%d ",root[L][R]); firstPrint(L,root[L][R]-1); firstPrint(root[L][R]+1,R); } int main() { int n; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&f[i][i]); root[i][i]=i; } printf("%d\n",dp(1,n)); firstPrint(1,n); printf("\n"); system("pause"); return 0; }
题目描述 Description
N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足T1<...<Ti>Ti+1>…>TK(1<=i<=K)。
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
输入文件chorus.in的第一行是一个整数N(2<=N<=100),表示同学的总数。第一行有n个整数,用空格分隔,第i个整数Ti(130<=Ti<=230)是第i位同学的身高(厘米)。
输出文件chorus.out包括一行,这一行只包含一个整数,就是最少需要几位同学出列。
8
186 186 150 200 160 130 197 220
4
对于50%的数据,保证有n<=20;
对于全部的数据,保证有n<=100。
此题可以用最长上升子序列和最长下降子序列做,不过有些细节需要处理,如图
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXN 300 int high[MAXN]; //high[i]=第i个同学的身高 int up[MAXN],dn[MAXN]; int max(int a,int b) { if(a>b) return a; return b; } int main() { int n; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&high[i]); up[i]=dn[i]=1; } for(int i=1;i<=n;i++) for(int j=1;j<=i;j++) if(high[j]<high[i]) up[i]=max(up[i],up[j]+1); for(int i=n;i>=1;i--) for(int j=n;j>=i;j--) if(high[j]<high[i]) dn[i]=max(dn[i],dn[j]+1); int maxPeople=-1; //最多参与人数 for(int i=1;i<=n;i++) maxPeople=max(maxPeople,dn[i]+up[i]-1); printf("%d\n",n-maxPeople); system("pause"); return 0; }
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。
题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。
输入第一行有一个正整数L(1<=L<=109),表示独木桥的长度。第二行有三个正整数S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离,及桥上石子的个数,其中1<=S<=T<=10,1<=M<=100。第三行有M个不同的正整数分别表示这M个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。
输出只包括一个整数,表示青蛙过河最少需要踩到的石子数。
10
2 35
2 3567
2
数据规模
对于30%的数据,L<=10000;
对于全部的数据,L<=109。
这个题的DP方程比较好推出来,f[i]=青蛙到达点i至少要踩多少个石头,那么DP方程是
f[i]=min{f[j]},i点无石头 f[i]=min{f[j]}+1,i点有石头但是题目很鬼畜,如果纯粹按照距离动规的话,总距离可能达到10^9左右(int的最大范围),空间时间都吃不消,不过1<=M<=100,石头个数不多,这说明可能有两个石头之间的距离太大,青蛙跳不过去,可以采取压缩状态的方式,缩短总距离,就是说如果两个石头之间距离太大,青蛙跳不过去的话,把两个石头之间的距离改成T,可以想到这样的距离青蛙也跳不过去,两者是等价的
具体可以看我之前的一个代码http://blog.csdn.net/qpswwww/article/details/25742233
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <algorithm> #define MAXN 10020 #define INF 0x3f3f3f3f using namespace std; int f[MAXN]; //f[i]=青蛙到达点i至少要踩的石子数 int maxStoneDist=0; //当前最远的石头距离 int river[MAXN]; //river[i]=1 int stonePos[MAXN]; int min(int a,int b) { if(a<b) return a; return b; } int main() { int L,S,T,M; memset(f,INF,sizeof(f)); scanf("%d%d%d%d",&L,&S,&T,&M); river[0]=1; f[0]=0; int dist,lastDist=0; for(int i=1;i<=M;i++) scanf("%d",&stonePos[i]); sort(stonePos+1,stonePos+M+1); //防坑,按距离对石头位置升序排序 for(int i=1;i<=M;i++) { if(stonePos[i]-stonePos[i-1]>=T) //新的石头太远,青蛙一定跳不过去 { maxStoneDist+=T; river[maxStoneDist]=1; //则设新石头距离为T+1 } else { maxStoneDist=maxStoneDist+stonePos[i]-stonePos[i-1]; river[maxStoneDist]=1; //否则新石头距离不变 } } for(int i=1;i<=maxStoneDist+T;i++) //我为人人型动规,跳跃起点为i { int minn=INF; for(int j=i-T;j<=i-S;j++) if(j>=0) minn=min(minn,f[j]); f[i]=minn+river[i]; } int ans=INF; //答案,从maxStoneDist到maxStoneDist+T中找 for(int i=maxStoneDist;i<=maxStoneDist+T;i++) ans=min(ans,f[i]); if(S==T) //最大跳跃距离等于最小跳跃距离,这种情况要单独讨论 { ans=0; for(int i=1;i<=M;i++) if(stonePos[i]%T==0) ans++; } printf("%d\n",ans); return 0; }
丽江河边有 n 家很有特色的客栈,客栈按照其位置顺序从1 到n 编号。每家客栈都按照某一种色调进行装饰(总共k 种,用整数0 ~ k-1 表示),且每家客栈都设有一家咖啡店,每家咖啡店均有各自的最低消费。
两位游客一起去丽江旅游,他们喜欢相同的色调,又想尝试两个不同的客栈,因此决定分别住在色调相同的两家客栈中。晚上,他们打算选择一家咖啡店喝咖啡,要求咖啡店位于两人住的两家客栈之间(包括他们住的客栈),且咖啡店的最低消费不超过p。
他们想知道总共有多少种选择住宿的方案,保证晚上可以找到一家最低消费不超过p元的咖啡店小聚。
共n+1 行。
第一行三个整数 n,k,p,每两个整数之间用一个空格隔开,分别表示客栈的个数,色调的数目和能接受的最低消费的最高值;
接下来的 n 行,第i+1 行两个整数,之间用一个空格隔开,分别表示i 号客栈的装饰色调和i 号客栈的咖啡店的最低消费。
输出只有一行,一个整数,表示可选的住宿方案的总数。
5 2 3
0 5
1 3
0 2
1 4
1 5
3
【输入输出样例说明】
客栈编号 ① ② ③ ④ ⑤ 色调 0 1 0 1 1 最低消费 5 3 2 4 5 2 人要住同样色调的客栈,所有可选的住宿方案包括:住客栈①③,②④,②⑤,④⑤,
但是若选择住 4、5 号客栈的话,4、5 号客栈之间的咖啡店的最低消费是 4,而两人能承受
的最低消费是 3 元,所以不满足要求。因此只有前 3 种方案可选。
【数据范围】
对于 30%的数据,有n≤100;
对于 50%的数据,有n≤1,000;
对于 100%的数据,有2≤n≤200,000,0<k≤50,0≤p≤100, 0≤最低消费≤100。
之前我曾写过一篇该题的解题报告,鉴于没有图加上数组变量太多,不便于理解,我重新写一篇题解
对于此题,可以用cheapMaxNum[i]表示1~i中编号最大的便宜客栈
colorMaxNum[i]表示1~i-1中与i颜色相同的编号最大的客栈
sameColorNum[i]表示1~i-1中和i颜色相同的客栈个数
ans[i]表示1~i-1中与i颜色相同,且其到i之间有便宜客栈的个数
colorNum[i]表示之前所有客栈中色调为i的个数
maxNum[i]表示之前所有客栈中色调为i的最大客栈编号
#include <stdio.h> #include <string.h> #include <stdlib.h> #define MAXN 200100 int cheapMaxNum[MAXN],colorMaxNum[MAXN],sameColorNum[MAXN],ans[MAXN],colorNum[MAXN],maxNum[MAXN]; /* 声明:便宜的客栈就是价格低于最高消费的客栈 cheapMaxNum[i]=1~i中编号最大的便宜客栈 colorMaxNum[i]=1~i-1中与i颜色相同的编号最大的客栈 sameColorNum[i]=1~i-1中和i颜色相同的客栈个数 ans[i]=1~i-1中与i颜色相同,且其到i之间有便宜客栈的个数 DP方程为: ans[i]=ans[colorMaxNum[i]],cheapMaxNum[i]<=colorMaxNum[i] ans[i]=sameColorNum[i],cheapMaxNum[i]>colorMaxNum[i] colorNum[i]=之前所有客栈中色调为i的个数,maxNum[i]=之前所有客栈中色调为i的最大客栈编号 */ int main() { int n,k,p,color,price; scanf("%d%d%d",&n,&k,&p); for(int i=1;i<=n;i++) { scanf("%d%d",&color,&price); colorMaxNum[i]=maxNum[color]; //前i-1个客栈中与i相同颜色的客栈最大编号就是之前color色调的最大客栈编号 sameColorNum[i]=colorNum[color]; //同理 if(price<=p) //i号客栈是便宜客栈 cheapMaxNum[i]=i; //前i个客栈中便宜客栈的最大编号就是i自己 else cheapMaxNum[i]=cheapMaxNum[i-1]; //否则前i个客栈中便宜客栈最大编号和前i-1个的相同 if(cheapMaxNum[i]<colorMaxNum[i]) ans[i]=ans[colorMaxNum[i]]; else ans[i]=sameColorNum[i]; colorNum[color]++; maxNum[color]=i; } int Ans=0; for(int i=2;i<=n;i++) //找客栈尾巴,统计方案总个数 Ans+=ans[i]; printf("%d\n",Ans); system("pause"); return 0; }
设r是个2k进制数,并满足以下条件:
(1)r至少是个2位的2k进制数。
(2)作为2k进制数,除最后一位外,r的每一位严格小于它右边相邻的那一位。
(3)将r转换为2进制数q后,则q的总位数不超过w。
在这里,正整数k(1≤k≤9)和w(k<W< span>≤30000)是事先给定的。
问:满足上述条件的不同的r共有多少个?
我们再从另一角度作些解释:设S是长度为w的01字符串(即字符串S由w个“0”或“1”组成),S对应于上述条件(3)中的q。将S从右起划分为若干个长度为k的段,每段对应一位2k进制的数,如果S至少可分成2段,则S所对应的二进制数又可以转换为上述的2k进制数r。
例:设k=3,w=7。则r是个八进制数(23=8)。由于w=7,长度为7的01字符串按3位一段分,可分为3段(即1,3,3,左边第一段只有一个二进制位),则满足条件的八进制数有:
2位数:高位为1:6个(即12,13,14,15,16,17),高位为2:5个,…,高位为6:1个(即67)。共6+5+…+1=21个。
3位数:高位只能是1,第2位为2:5个(即123,124,125,126,127),第2位为3:4个,…,第2位为6:1个(即167)。共5+4+…+1=15个。
所以,满足要求的r共有36个。
只有1行,为两个正整数,用一个空格隔开:
k W
共1行,是一个正整数,为所求的计算结果,即满足条件的不同的r的个数(用十进制数表示),要求最高位不得为0,各数字之间不得插入数字以外的其他字符(例如空格、换行符、逗号等)。
(提示:作为结果的正整数可能很大,但不会超过200位)
3 7
36
#include <iostream> #include <string.h> #include <stdio.h> #define BASE 10000 #define MAXN 512 using namespace std; struct Hugeint { int c[100],len,sign; Hugeint(){memset(c,0,sizeof(c));len=1;sign=0;} void zero() { while(len>1&&c[len]==0) len--; //清除前面的0 if(len==1&&c[len]==0) sign=0; } void writein(char *s) //将数字串s压4位存入高精数组中 { int k=1,L=strlen(s); //L=s的长度 for(int i=L-1;i>=0;i--) { c[len]+=(s[i]-'0')*k; k*=10; if(k==BASE) //这一位装不下了 { k=1; len++; //换到下一位 } } } void read() //读入高精度数 { char s[300]={0}; scanf("%s",s); writein(s); } void print() { if(sign) printf("-"); //负数 printf("%d",c[len]); for(int i=len-1;i>=1;i--) printf("%04d",c[i]); printf("\n"); } Hugeint operator = (int a) //给高精度赋值 { char s[300]={0}; sprintf(s,"%d",a); writein(s); return *this; //this指针只能用于成员函数中,表示当前对象的地址 } Hugeint operator+(const Hugeint &b) { Hugeint r; r.len=max(len,b.len)+1; for(int i=1;i<=r.len;i++) { r.c[i]+=c[i]+b.c[i]; r.c[i+1]+=r.c[i]/BASE; //算上进位 r.c[i]%=BASE; } r.zero(); //清掉前面的零 return r; } Hugeint operator+(const int &a) //高精加低精 { Hugeint b; b=a; return *this+b; } }C[MAXN][MAXN],ans; int main() { int k,w,maxNum,firstNum; cin>>k>>w; maxNum=1<<k; //maxNum=2^k,每一位可以选择的数字为1~maxNum-1 firstNum=1<<(w%k); //首段最大可取数字为firstNum-1 C[0][0]=1; for(int i=1;i<maxNum;i++) for(int j=0;j<=i;j++) //在i个数里选j个数的方案个数 { if(j==0) C[i][j]=1; else C[i][j]=C[i-1][j]+C[i-1][j-1]; } for(int i=2;i<=w/k&&i<maxNum;i++) //i=2k进制数的位数 ans=ans+C[maxNum-1][i]; for(int i=1;i<firstNum&&w/k+i<maxNum;i++) //首位数字取i ans=ans+C[maxNum-i-1][w/k]; ans.print(); return 0; }