A - 外币兑换
蒜头君刚从美国回来,发现手上还有一些未用完的美金,于是想去银行兑换成人民币。可是听说最近人民币将会升值,并从金融机构得到了接下来十二个月可能的美元对人民币汇率,现在,蒜头君想要在接下来一年中把美金都兑换成人民币,请问最多能得到多少人民币?
输入格式
输入的第一行是一个实数 N(1.00≤N≤100.00),表示蒜头君现有的美金数量。
接下来一行,包含 12 个实数 ai,表示接下来十二个月的美元对人民币汇率。
输出格式
输出一个小数 R,表示蒜头君最多能获得的人民币数量,结果保留两位小数。
Sample 1
Input | Output |
---|---|
46.91 6.31 6.32 6.61 6.65 5.55 5.63 6.82 6.42 6.40 5.62 6.78 5.60 | 319.93 |
比较简单的题目,找出最大的汇率相乘即可
#include <iostream>
#include <cstdio>
using namespace std;
double a[12];
int main()
{
int i;
double ans,money,maxn=0;
cin>>money;
for (i=0;i<12;i++) {
cin>>a[i];
if (a[i]>maxn) {
maxn=a[i];
}
}
ans=maxn*money;
printf("%.2f",ans);
return 0;
}
B - 删数问题
键盘输入一个高精度的正整数 n,去掉其中任意 k 个数字后剩下的数字按原左右次序将组成一个新的正整数。编程对给定的 n,k,寻找一种方案使得剩下的数字组成的新数最小。
输出应包括所去掉的数字的位置和组成的新的整数。
输入格式
第一行一个高精度整数 n,n 不超过 200 位。
第二行一个整数 k,k 小于 n 的位数,表示要删去的数字个数。
输出格式
一个合法的整数,表示最后剩下的最小数。
Sample 1
Input | Output |
---|---|
175438 4 | 13 |
Sample 2
Input | Output |
---|---|
101 2 | 0 |
贪心思想,局部最优解最后满足最优解
当前一个数大于后面一个数时,即可让后面的数覆盖前面的数,这样就可使局部满足最小。
同样有一种情况就是所有的数都是前面的小于后面的,这样就选择删除后面的最大的数。
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn=210;
char a[maxn];
int main()
{
int m,n,i;
cin>>a>>m;
n=strlen(a);
while (m--) {
for (i=0;i<n-1;i++) {
if (a[i]>a[i+1]) {
for (int t=i;t<n-1;t++) {
a[t]=a[t+1];
}
break;
}
}
n--;//具有删除最后一个数的功能
}
int cont=0;
while (cont<n-1) {
if (a[cont]=='0') {
cont++;
} else {
break;
}
}
for (i=cont;i<n;i++) {
cout<<a[i];
}
return 0;
}
C - 股票买卖
最近越来越多的人都投身股市,阿福也有点心动了。谨记着“股市有风险,入市需谨慎”,阿福决定先来研究一下简化版的股票买卖问题。
假设阿福已经准确预测出了某只股票在未来 N 天的价格,他希望买卖两次,使得获得的利润最高。为了计算简单起见,利润的计算方式为卖出的价格减去买入的价格。
同一天可以进行多次买卖。但是在第一次买入之后,必须要先卖出,然后才可以第二次买入。
现在,阿福想知道他最多可以获得多少利润。
Input
输入的第一行是一个整数 T (T <= 50) ,表示一共有 T 组数据。
接下来的每组数据,第一行是一个整数 N (1 <= N <= 100, 000) ,表示一共有 N 天。第二行是 N 个被空格分开的整数,表示每天该股票的价格。该股票每天的价格的绝对值均不会超过 1,000,000 。
Output
对于每组数据,输出一行。该行包含一个整数,表示阿福能够获得的最大的利润。
Sample
Input | Output |
---|---|
3 7 5 14 -2 4 9 3 17 6 6 8 7 4 1 -2 4 18 9 5 2 | 28 2 0 |
Hint
对于第一组样例,阿福可以第 1 次在第 1 天买入(价格为 5 ),然后在第 2 天卖出(价格为 14 )。第 2 次在第 3 天买入(价格为 -2 ),然后在第 7 天卖出(价格为 17 )。一共获得的利润是 (14 - 5) + (17 - (-2)) = 28
对于第二组样例,阿福可以第 1 次在第 1 天买入(价格为 6 ),然后在第 2 天卖出(价格为 8 )。第 2 次仍然在第 2 天买入,然后在第 2 天卖出。一共获得的利润是 8 - 6 = 2
对于第三组样例,由于价格一直在下跌,阿福可以随便选择一天买入之后迅速卖出。获得的最大利润为 0
这里提供两种做法
1.贪心
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int mn[maxn],ma[maxn],dp1[maxn],dp2[maxn],a[maxn];
int main()
{
int m;
scanf("%d",&m);
while (m--) {
int n,i;
scanf("%d",&n);
for (i=1;i<=n;i++) {
scanf("%d",&a[i]);
}
mn[1]=a[1];
dp1[1]=0;
for (i=2;i<=n;i++) {
mn[i]=min(mn[i-1],a[i]);//在i天之前的价格的最小值
dp1[i]=max(dp1[i-1],a[i]-mn[i]);//第i天的最大利润
}
dp2[n]=0;
ma[n]=a[n];
for (i=n-1;i>=1;i--) {
ma[i]=max(ma[i+1],a[i]);//在i天之后的最大价格
dp2[i]=max(dp2[i+1],ma[i]-a[i]);//第i天以后的最大利润
}
int ans=0;
for (i=1;i<=n;i++) {
if (ans<dp1[i]+dp2[i]) {
ans=dp1[i]+dp2[i];
}
}
printf("%d\n",ans);
}
return 0;
}
2.动态规划
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int a[maxn],dp[maxn][5];//这里的0 1 2 3 4 5分别表示无操作 第一次买入 第一次卖出 第二次买入 第二次卖出
int main()
{
int m;
scanf("%d",&m);
while (m--) {
int n,i;
scanf("%d",&n);
for (i=0;i<n;i++) {
scanf("%d",&a[i]);
}
dp[0][1]=-a[0];
dp[0][3]=-a[0];
for (i=1;i<n;i++) {
dp[i][1]=max(dp[i-1][0]-a[i],dp[i-1][1]);
dp[i][2]=max(dp[i-1][2],dp[i-1][1]+a[i]);
dp[i][3]=max(dp[i-1][3],dp[i-1][2]-a[i]);
dp[i][4]=max(dp[i-1][4],dp[i-1][3]+a[i]);
}
printf("%d\n",dp[n-1][4]);
}
return 0;
}
D - 数列分段
对于给定的一个长度为 NN 的正整数数列 Ai,现要将其分成连续的若干段,并且每段和不超过 M(可以等于 M),问最少能将其分成多少段使得满足要求。
输入格式
第一行包含两个正整数 N,M,表示了数列 Ai 的长度与每段和的最大值;
第二行包含 N 个空格隔开的非负整数 Ai。
输出格式
输出文件仅包含一个正整数,输出最少划分的段数。
样例
Input | Output |
---|---|
5 6 4 2 4 5 1 | 3 |
数据范围与提示
对于 20% 的数据,有N≤10;
对于 40% 的数据,有N≤1000;
对于 100% 的数据,有 N≤105,M≤109,M 大于所有数的最大值。
这个直接暴力即可
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int a[maxn];
int main()
{
int m,n,i,cont=1;
cin>>n>>m;
ll sum=0;
for (i=0;i<n;i++) {
cin>>a[i];
if (sum+a[i]>m) {
sum=a[i];
cont++;
} else {
sum+=a[i];
}
}
cout<<cont<<endl;
return 0;
}
E - 最大子段和
N个整数组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的连续子段和的最大值。当所给的整数均为负数时和为0。
例如:-2,11,-4,13,-5,-2,和最大的子段为:11,-4,13。和为20。
Input
第1行:整数序列的长度N(2 <= N <= 50000) 第2 - N + 1行:N个整数(-10^9 <= A[i] <= 10^9)
Output
输出最大子段和。
Sample
Input | Output |
---|---|
6 -2 11 -4 13 -5 -2 | 20 |
求最大子段和,一旦前面的和出现负数,就会影响后面的最大值,所以当出现负数时应舍去
#include <iostream>
using namespace std;
typedef long long ll;
const int maxn=5e4+10;
int a[maxn];
int main()
{
int n,i;
ll ans=0;
cin>>n;
for (i=0;i<n;i++) {
cin>>a[i];
}
ll sum=0;
for (i=0;i<n;i++) {
sum+=a[i];
ans=max(ans,sum);
if (sum<0) {//舍去负数
sum=0;
}
}
cout<<ans<<endl;
return 0;
}
F - 活动安排问题
有若干个活动,第i个开始时间和结束时间是[Si,fi),同一个教室安排的活动之间不能交叠,求要安排所有活动,最少需要几个教室?
Input
第一行一个正整数n代表活动的个数。 之后n行每行两个数s,t,分别为开始/结束时间。 其中n<=10000,0<=t1 <1e9。
Output
一行包含一个整数表示最少教室的个数。
Sample
Input | Output |
---|---|
3 1 2 3 4 2 9 | 2 |
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e4+10;
typedef struct date{
int x;
int y;
int z;
}SJ;
ll b[maxn];
bool cmp(SJ a,SJ b) {
if (a.x==b.x) {
return a.z<b.z;
} else {
return a.x<b.x;
}
}
int main()
{
int n,i,sum=1,t;
SJ a[maxn];
cin>>n;
for (i=0;i<n;i++) {
cin>>a[i].x>>a[i].y;
a[i].z=a[i].y-a[i].x;
}
sort(a,a+n,cmp);
b[0]=a[0].y;
for (i=1;i<n;i++) {
for (t=0;t<sum;t++) {
if (a[i].x>=b[t]) {
b[t]=a[i].y;
break;
}
}
if (t==sum) {//创建新房间
b[sum]=a[i].y;
sum++;
}
}
cout<<sum<<endl;
return 0;
}
G - 线段
数轴上有 nn 条线段,选取其中 kk 条线段使得这 kk 条线段两两没有重合部分,问 kk 最大为多少。
输入格式
第一行为一个正整数 nn;
在接下来的 nn 行中,每行有 22 个数 a_i, b_iai,bi,描述每条线段。
输出格式
输出一个整数,为 kk 的最大值。
样例
Input | Output |
---|---|
3 0 2 2 4 1 3 | 2 |
数据范围与提示
对于 20% 的数据,n≤10;
对于 50% 的数据,n≤103;
对于 70% 的数据,n≤105;
对于 100% 的数据,n≤106, 0≤ai<bi≤106。
与老板开会问题为一个类型的题目,先进行排序,然后就求取最大数量
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
struct date {
int a;
int b;
int c;
}a[maxn];
bool cmp (date e,date f){
if (e.b==f.b) {
return e.c<f.c;
} else {
return e.b<f.b;
}
}
int main()
{
int n,i;
cin>>n;
for (i=0;i<n;i++) {
cin>>a[i].a>>a[i].b;
a[i].c=a[i].b-a[i].a;
}
sort(a,a+n,cmp);
int cont=1,end=a[0].b;
for (i=1;i<n;i++) {
if (a[i].a>=end) {
cont++;
end=a[i].b;
}
}
cout<<cont<<endl;
return 0;
}
H - 定价
在市场上有很多商品的定价类似于 999 元、4999 元、8999 元这样。它们和 1000 元、5000 元和 9000 元并没有什么本质区别,但是在心理学上会让人感觉便宜很多,因此也是商家常用的价格策略。不过在你看来,这种价格十分荒谬。于是你如此计算一个价格 pp(pp 为正整数)的荒谬程度:
- 首先将 pp 看做一个由数字组成的字符串(不带前导 00);
- 然后,如果 pp 的最后一个字符是 00,就去掉它。重复这一过程,直到 pp 的最后一个字符不是 00;
- 记 pp 的长度为 aa,如果此时 pp 的最后一位是 55,则荒谬程度为 2a - 12a−1;否则为 2a2a。
例如,850 的荒谬程度为 3,而 880 则为 4,9999 的荒谬程度为 8。
现在,你要出售一样闲置物品,你能接受的定价在 [L, R][L,R] 范围内,你想要给出一个荒谬度最低的价格。
输入格式
输入文件的第一行包含一个正整数 T,表示测试数据的数目。
每个测试数据占单独的一行,包含两个空格分隔的正整数 L,R,表示定价的区间。
输出格式
对于每个测试数据,在单独的一行内输出结果。如果荒谬度最低的价格不唯一,输出最小的那个。
样例
Input | Output |
---|---|
3 998 1002 998 2002 4000 6000 | 1000 1000 5000 |
数据范围与提示
对于 100% 的数据,T≤100, 1≤L≤R≤109。
刚遇到这题时有点蒙,然后参考了别人的代码传送门
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
int a[15]={1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};
int jia (int t) {
int shu=0;
while (t%10==0) {
t/=10;
shu++;
}
return shu;
}
int las (int t) {
while (t%10==0) t/=10;
return t%10;
}
int tongji (int t) {
int cont=0;
while (t%10==0) t/=10;
while (t>0) {
t/=10;
cont++;
}
return cont;
}
int main()
{
int m;
cin>>m;
while (m--) {
int l,r,i;
int ans,sign=99999;
cin>>l>>r;
for (i=l;i<=r;i+=a[jia(i)]) {
int last;
if (las(i)==5) {
last=1;
} else {
last=0;
}
if (sign>2*tongji(i)-last) {
sign=2*tongji(i)-last;
ans=i;
}
}
cout<<ans<<endl;
}
return 0;
}
I - 排队接水
n个人一起排队接水,第i个人需要b[i]的时间来接水。(1<=n<=1000,0<=b[i]<=1000)
同时只能有一个人接水,正在接水的人和没有接水的人都需要等待。
完成接水的人会立刻消失,不会继续等待。
你可以决定所有人接水的顺序,并希望最小化所有人等待时间的总和。
Input
第一行一个整数n 接下来n行,每行一个整数表示b[i]
Output
一行一个整数,表示所有人等待时间的总和的最小值
Sample
Input | Output |
---|---|
3 1 2 3 | 10 |
思考一下,当一个人在接水时,后面的人同样在等,所以要让时间短的人先接,这样可以让后面的人等的时间短一些
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=1010;
int a[maxn];
bool cmp (int c,int b) {
return c<b;//升序排列
}
int main()
{
int n,i;
cin>>n;
for (i=0;i<n;i++) {
cin>>a[i];
}
sort(a,a+n,cmp);
ll sum=a[0],cont=0;
for (i=1;i<n;i++) {
cont+=a[i-1];
sum+=cont+a[i];
}
cout<<sum<<endl;
return 0;
}
J - 拼成最小的数 V2
设有n个正整数,将它们连接成一排,组成一个最小的多位整数。
例如:
n=2时,2个整数32,321连接成的最小整数为:32132,
n=4时,4个整数55,31,312, 33 联接成的最小整数为:312313355
Input
第1行:1个数N。(2 <= N <= 10000) 第2 - N+1行:每行1个正整数。(1 <= A[i] <= 10^9)
Output
输出拼在一起的最小整数。
Sample
Input | Output |
---|---|
4 55 31 312 33 | 312313355 |
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
typedef long long ll;
const int maxn=1e4+10;
string a[maxn];
bool cmp (string a,string b) {
return a+b<b+a;//string在这里可以直接进行拼接
}
int main()
{
int n,i;
cin>>n;
for (i=0;i<n;i++) {
cin>>a[i];
}
sort(a,a+n,cmp);
for (i=0;i<n;i++) {
cout<<a[i];
}
cout<<endl;
return 0;
}
L - 货币系统
在网友的国度中共有 n 种不同面额的货币,第 i 种货币的面额为 a[i],你可以假设每一种货币都有无穷多张。为了方便,我们把货币种数为 n、面额数组为 a[1\dots n]a[1…n] 的货币系统记作 (n,a)。
在一个完善的货币系统中,每一个非负整数的金额 x 都应该可以被表示出,即对每一个非负整数 x,都存在 n 个非负整数 t[i] 满足 a[i] \times t[i]a[i]×t[i] 的和为 x。然而,在网友的国度中,货币系统可能是不完善的,即可能存在金额 x 不能被该货币系统表示出。例如在货币系统 n=3, a=[2,5,9] 中,金额 1,3 就无法被表示出来。
两个货币系统 (n,a) 和 (m,b) 是等价的,当且仅当对于任意非负整数 x,它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。
现在网友们打算简化一下货币系统。他们希望找到一个货币系统 (m,b),满足 (m,b) 与原来的货币系统 (n,a) 等价,且 m 尽可能的小。他们希望你来协助完成这个艰巨的任务:找到最小的 m。
Input
输入文件的第一行包含一个整数 T,表示数据的组数。 接下来按照如下格式分别给出 T 组数据。 每组数据的第一行包含一个正整数 n。接下来一行包含 n 个由空格隔开的正整数 a[i]。
Output
输出文件共有 T 行,对于每组数据,输出一行一个正整数,表示所有与 (n,a) 等价的货币系统 (m,b) 中,最小的 m。
Sample
Input | Output |
---|---|
2 4 3 19 10 6 5 11 29 13 19 17 | 2 5 |
这题昨天看完别人的题解后才明白可以转化成一个完全背包问题,用小的价钱来表示出大的价钱
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
bool dp[maxn];
int a[110];
bool cmp (int a,int b) {
return a<b;
}
int main()
{
int m;
cin>>m;
while (m--) {
int n,i;
cin>>n;
int mx=0;
memset(dp,false,sizeof(dp));
for (i=0;i<n;i++) {
cin>>a[i];
mx=max(mx,a[i]);
}
int ans=0;
sort(a,a+n,cmp);
dp[0]=true;
for (i=0;i<n;i++) {
if (dp[a[i]]==0) {//这里是看当前的价格是否已经被表示出来了
ans++;
}
for (int t=0;t<=mx;t++) {
if (dp[t]) {//如果该价格表示出来以后就加上的当前的货币来表示下一个价格
dp[t+a[i]]=1;
}
}
}
cout<<ans<<endl;
}
return 0;
}
N - 接水问题二
n 个人一起排队接水,第 ii 个人的重要性是 a[i]a[i],需要 b[i]b[i] 的时间来接水。(1 <= n <= 100000,0 <= b[i] <= 1000,0 <= a[i] <= 1000)
同时只能有一个人接水,正在接水的人和没有接水的人都需要等待。
完成接水的人会立刻消失,不会继续等待。
你可以决定所有人接水的顺序,并希望最小化所有人等待时间乘以自己的重要性 a[i]a[i] 的总和,输出等待时间乘以自己的重要性的总和。
Input
第一行一个整数n。 以下n行,每行两个整数a[i]和b[i]。
Output
一行一个整数表示答案。
Sample
Input | Output |
---|---|
4 1 4 2 3 3 2 4 1 | 35 |
这个问题多了一个变量,那就不能只单单考虑时间的问题
a[1] a[2] b[1] b[2]
先让a接水的时间:a[1]*a[2]+b[1]*(a[2]+b[2])
先让b接水的时间:b[1]*b[2]+a[1]*(a[2]+b[2])
相减就会发现就剩下b[1]a[2]和a[1]*b[2],那我们就根据这两项排序即可
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
struct date {
int a;
int b;
}a[maxn];
bool cmp(date c,date d) {
return d.a*c.b<c.a*d.b;
}
int main()
{
int n,i;
cin>>n;
for (i=0;i<n;i++) {
cin>>a[i].a>>a[i].b;
if (a[i].a==0||a[i].b==0) {
i--;
n--;
}
}
sort(a,a+n,cmp);
ll ans=a[0].b*a[0].a,sum=0;
for (i=1;i<n;i++) {
sum+=a[i-1].b;
ans+=(sum+a[i].b)*a[i].a;
}
cout<<ans<<endl;
return 0;
}