第四次考试大整理
第一题
多重背包
(backpack.cpp/c/pas)
(1s/256M)
题目描述
提供一个背包,它最多能负载重量为W的物品。
现在给出N种物品:对于第i类物品,一共有Ci件物品;对于每一件物品,重量为Wi,价值为Vi。
找出一种装载方式使得背包中的物品总价值最大。
输入格式(backpack.in)
第一行两个整数N,W,代表物品的种类与背包的总负重。
第2~N+1行,每行三个整数Wi, Vi, Ci,代表第i种物品的重量、价值与数量。
(即同种背包拥有很多个)
输出格式(backpack.out)
仅一行,一个整数V,代表最大的总价值。
样例输入
3 9
5 8 2
3 6 2
2 1 5
样例输出
14
数据范围与限制
1<=N<=20, 0<=W<=1000
1<=Wi<=100, 0<=Vi<=100, 0<=Ci<=100
ps:(老师说他的数据范围给错了???!!!exm?)
超时代码情况:
20个测试点,只得了55分,其他全部T……
思路:
改成类之后,仅仅是将背包的数量变多,多了许多相同价值的背包出来,
那么,我们就可以将它转为01背包的方法,全部枚举一遍,可惜的是,超时
代码如下:
1 #include<iostream>
2 #include<cstring>
3 #include<cstdio>
4 #include<algorithm>
5 #include<cmath>
6 #include<cstdlib>
7 #include<fstream> // 记住多include这一句
8 #define M 2017
9
10 using namespace std;
11
12 int n,w;//物品的'种类' 与 背包的总负重
13 int ans=0;
14 int wi[M],vi[M],ci[M];//重量、价值与数量
15
16 void dfs(int x,int noww,int nowv,int nownum)
17 {
18 if(x>n)//限制范围
19 {
20 if(nowv>ans)
21 {
22 ans=nowv;//记录答案
23 }
24 return;
25 }
26
27 if(nownum<ci[x])//如果这类的还有背包
28 {
29 dfs(x,noww,nowv,nownum+1);//继续进行搜索
30 }
31 else dfs(x+1,noww,nowv,1);
32
33 if(noww+wi[x]<=w)//若不超出总质量
34 {
35 if(nownum<ci[x])//如果这类的还有背包
36 {
37 dfs(x,noww+wi[x],nowv+vi[x],nownum+1);//继续进行搜索
38 }
39 else dfs(x+1,noww+wi[x],nowv+vi[x],1);
40 }
41 }
42
43 int main()
44 {
45 ifstream fin("backpack.in");
46 ofstream fout("backpack.out");
47
48 //scanf("%d%d",&n,&w);
49 fin>>n>>w;
50 for(int i=1;i<=n;i++)
51 {
52 //scanf("%d%d%d",&wi[i],&vi[i],&ci[i]);
53 fin>>wi[i]>>vi[i]>>ci[i];
54 }
55
56 dfs(1,0,0,1);
57
58 //printf("%d",ans);
59 fout<<ans<<endl;
60
61 return 0;
62 }
改进后思路:
直接将单个单个的枚举改为for循环,因为同种背包的价值是相同的,
所以不需要一个一个的枚举,仅仅需要改为“*”就可以!这一点是很重要的
改成下面之后就并没有超时,
代码如下:
1 //backpack
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<algorithm>
6 #include<cmath>
7 #include<cstdlib>
8 #include<fstream> // 记住多include这一句
9 #define M 2017
10
11 using namespace std;
12
13 int n,w;//物品的'种类' 与 背包的总负重
14 int ans=0;
15 int wi[M],vi[M],ci[M];//重量、价值与数量
16
17 void dfs(int x,int noww,int nowv)
18 {
19 if(x>n)//限制范围
20 {
21 if(nowv>ans)
22 {
23 ans=nowv;//记录答案
24 }
25 return;
26 }
27 for(int i=0;i<=ci[x]&&noww+wi[x]*i<=w;i++)
28 {
29 dfs(x+1,noww+wi[x]*i,nowv+vi[x]*i);
30 }
31 }
32
33 int main()
34 {
35 ifstream fin("backpack.in");
36 ofstream fout("backpack.out");
37
38 //scanf("%d%d",&n,&w);
39 fin>>n>>w;
40 for(int i=1;i<=n;i++)
41 {
42 //scanf("%d%d%d",&wi[i],&vi[i],&ci[i]);
43 fin>>wi[i]>>vi[i]>>ci[i];
44 }
45
46 dfs(1,0,0);
47
48 //printf("%d",ans);
49 fout<<ans<<endl;
50
51 return 0;
52 }
第二题
循环序列
(circulate.cpp/c/pas)
(1s/256M)
题目描述
Alice与Bob在玩游戏:
Alice首先给出两个数X与Y(X<=Y);
Bob则按顺序将X,X+1,X+2,…,Y-1,Y写成一个大数S。
Alice最后将S首尾相连,让其围成一个圈。
这时,Bob想知道,从S的开头出发,往后的第L位到第R位数字之和是多少。
输入格式(circulate.in)
第一行四个整数X,Y,L,R,代表Alice的两个数字和Bob想要知道的第L位到第R位的数字之和。
输出格式(circulate.out)
仅一行,一个整数M,代表第L位到第R位的数字之和。
样例输入
10 11 4 12
样例输出
7
样例解释
Bob将数字写成一行大数S = 1011;围成一个圈后,从第4位到第12位分别是1,1,0,1,1,1,0,1,1,它们的和是7.
数据范围与限制
对于50%的数据,L=1, X,Y,L,R<=1000;
对于100%的数据,S的长度不大于10000,X,Y,L,R<=100000000.
心得:
这道题其实做的时候我并没有想出来啦,(因为没读懂题目的意思……)但是在老师讲过了之后,我感觉豁然开朗了
代码+思路:
1 #include <cstdio>
2 #include <cstring>
3
4 using namespace std;
5
6 const int N = 1e5 + 6;
7
8 int x, y, l, r;
9 int n, a[N], s[N];
10 int m, b[N];
11
12 void work(int x, int y)
13 {
14 n = 0; //记录最终一共是多少位的数
15 for(int i=x; i<=y; i++) //因为题目中说道这是有规律的(从小到大),依次进行枚举这些数
16 {
17 m = 0; //m用来记录当前数的位数是多少
18 for(int t=i; t; t/=10) b[++m] = t%10; //将所有的数字都取出来
19 for(int t=m; t; t--) a[++n] = b[t]; //都放在最终(另外一个)的数组中,便于最终进行统计和
20 }
21
22 s[0] = 0; //前缀和数组
23 for(int i=1; i<=n; i++) s[i] = s[i-1] + a[i]; //进行前缀和处理
24 }
25
26 int cal(int p)
27 {
28 if(p == 0) return 0; //是当数据是从头开始的情况吧
29
30 int g = (p-1) / n; //能够有多少个"区间"
31 int r = (p-1) % n + 1; //取出最后一个区间的最右边的前缀和
32
33 return s[n] * g + s[r]; //直接返回正确答案
34 }
35
36 int main()
37 {
38 freopen("circulate.in", "r", stdin);
39 freopen("circulate.out", "w", stdout);
40
41 scanf("%d%d", &x, &y); //输入的两个数字
42 scanf("%d%d", &l, &r); //区间范围
43
44 work(x, y);
45
46 printf("%d\n", cal(r) - cal(l-1)); //类似于前缀和??
47
48 return 0;
49 }
第三题
合并游戏
merge.cpp/c/pas
(1s/256M)
题目描述
Cindy和Dan在玩一个游戏。
一开始Cindy想出了N个数,接着她把这N个数全部给了Dan。
Dan得到这组数后,它会挑出3个数(如果不足3个则全部挑出)。Dan会把这几个数加起来变成一个数,然后再把这个数与剩下的数再放到一起。Dan会一直这样做,直到最后只剩下一个数。
Cindy则会在旁边记下每次Dan得到的数,她把这些数加起来,作为本次游戏的得分。她想知道,对于一组数,Dan能得到的最大的得分是多少?
输入格式
第一行一个正整数N,代表这组数的个数;
第二行N个正整数,代表这N个整数。
输出格式
一行一个整数,代表可能的最大得分。
样例输入(merge.in)
4
3 1 5 6
样例输出(merge.out)
29
样例解释
Dan可以首先把(3,5,6)这三个数先合并起来,得到3 + 5 + 6 = 14; 接着他把剩下的两个数再合起来,得到1 + 14 = 15.这样,总得分是最大的 14 + 15 = 29.
数据范围与限制
对于50%的数据,N<=10
对于100%的数据,N<=1000,所有数不大于1000
拉呱:
这道题可能是这三道题中最为简单的题了吧!这道题我A掉了,不过可惜的是,其他人并没有意识到这道题很简单,一直在研究第二题,……第二题我完全不会,就靠这个得的分
所以一定要大体看一下题目的难易程度,然后再做题!
思路:
这道题目是求“最大”的得分,那么很显然这就是贪心,(贪最大的),而且不需要担心顺序在合并之后的改变,因为3个最大的数加起来一定就是剩余的数中最大的数,所以,不用合并一次就进行排序什么的,那么这道题就很简单了。
因为是三个数三个数的进行合并,还有一个限制条件就是(如果不足三个就都挑出来)!这一点是非常重要的,所以他的结束条件就是当仅剩下3个数或者更加少的时候,直接进行合并,并且退出,输出答案。
代码:
1 //合并游戏//merge
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<algorithm>
6 #include<cmath>
7 #include<cstdlib>
8 #include<fstream> // 记住多include这一句
9 #define M 1010
10
11 using namespace std;
12
13 int n,maxn=0;
14 int ans=0,maxans=0;
15 int qnum[M];
16
17 void s(int nowsy,int nowans) //nowsy表示现在剩下的还有几个数
18 {
19 if(nowsy==1||nowsy==2||nowsy==3) //如果剩下的数都能够进行合并
20 {
21 if(nowans+maxn>maxans) //如果找到了最大的数值
22 {
23 maxans=nowans+maxn; //进行更新
24 }
25 return ;
26 }
27 int x1=nowsy,x2=nowsy-1,x3=nowsy-2;
28 qnum[x3]+=qnum[x1]+qnum[x2]; //将合并之后的数储存在x3中
29 ans+=qnum[x3]; //更新答案
30 s(nowsy-2,ans); //继续递归下去
31 }
32
33 int main()
34 {
35 ifstream fin("merge.in");
36 ofstream fout("merge.out");
37
38 //cin>>n;
39 fin>>n;
40 for(int i=1;i<=n;i++)
41 {
42 //cin>>qnum[i];
43 fin>>qnum[i];
44 maxn+=qnum[i];
45 /*
46 maxn是用来先记录下来这些书一共是多少,这样就可以在最后一次合并的时候,
47 不用再次计算一次和的计算,我是感觉挺好用啦,但我并不知道你是怎么想的。
48 */
49 }
50
51 sort(qnum+1,qnum+1+n); //从小到大排序
52 s(n,0);
53
54 //cout<<maxans<<endl;
55 fout<<maxans<<endl;
56
57 return 0;
58 }