第四次考试大整理

第一题

多重背包

(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
", 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 }

 

如果运气好也是错,那我倒愿意错上加错!

❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀

原文地址:https://www.cnblogs.com/zxqxwnngztxx/p/6783291.html