1.最大子段和
题目描述
给定有n个整数(可能为负整数)组成的序列a1,a2,...,an,求该序列连续的子段和的最大值。如果该序列的所有元素都是负整数时定义其最大子段和为0。
例如,当(a1,a2,a3,a4,a5)=(-5,11,-4,13,-4-2)时,最大子段和为11+(-4)+13=20。
输入
输入数据有多组测试数据。每组测试数据有两行:第一行整数个数N,第二行为N个整数,每个整数之间用一空格隔开。
输出
每个用例,用一行输出最大连续子段和。
样例输入 复制
6
-2 11 -4 13 -5 -2
样例输出 复制
20
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int a[100000];
int main()
{
int n,temp = 0, max = 0;
while (~scanf("%d", &n)) //多组输入
{
for (int i = 0; i < n; i++)
scanf("%d", a+i);
temp = 0, max = 0;//清零不要忘记了
for (int i = 0; i < n; i++)
{
temp += a[i];//依次将数组的元素累加到temp
if (temp > max)//如果累加的大于原先存入的max的时候把temp存入max(求最大子段和)
{
max = temp;
}
//如果temp的值小于0则清零(保证temp的累加为有效值)
if (temp <= 0)
{
temp = 0;
}
}
printf("%d\n", max);
}
return 0;
}
斐波那契数列
题目描述
编写一个求斐波那契数列的递归函数,输入n值,使用该递归函数,输出如样例输出的斐波那契数列。
输入
一个整型数n
输出
题目可能有多组不同的测试数据,对于每组输入数据,
按题目的要求输出相应的斐波那契图形。
样例输入 复制
6
样例输出 复制
0
0 1 1
0 1 1 2 3
0 1 1 2 3 5 8
0 1 1 2 3 5 8 13 21
0 1 1 2 3 5 8 13 21 34 55
提示
数据的最大值不会超过long long的范围
思路:斐波拉契数列的打印过程,我们可以发现n=1的时候打印一个,n=2的时候打印3个,n=3的时候打印五个,发现是个等差数列,因此我们循环的结束条件就算2*n-1,多组输入的话,我们需要把斐波拉契提前存入一个数组,这样打印的过程就不会反复调用而时间超限了
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int main()
{
int n;
while(~scanf("%d",&n))
{
long long a[10000]={0,1,1};//a[0]=0,a[1]=1,a[2]=1;
//提前把斐波拉契存入数组a中
for(int i=3;i<=2*n-2;i++)
{
a[i]=a[i-1]+a[i-2];
}
for(int i=1;i<=n;i++)
{
//这里应为是从0开始,所以打印结束条件是i*2-2(数组存值是从零开始的)
for(int j=0;j<=i*2-2;j++)
{
printf("%lld ",a[j]);
}
puts("");
}
}
}
无聊的跳爷
题目描述
最近眺爷无聊的玩起了数格点的游戏,现给定平面上的两个格点 p1 = (x1,y1) 和 p2 = (x2,y2), 他想找出线段p1p2上除p1和p2以外一共有几个格点?但他总是数错, 你能写个程序帮帮他吗?
输入
第一行 : x1,y1
第二行:x2,y2
输出
格点数
样例输入 复制
1 11
5 3
样例输出 复制
3
思路:我们可以先画图看看,我们需要得到的结论是格点数=|(x1-x2)|和|(y1-y2)|的最大公约数-1
(x1-x2)(y1-y2)(这个过程可以理解成把这根线移到原点的位置)
理解的话,我们可以想到格点数是整数,我们通过减法的话是把点与点之间在x轴与y轴上的差值
求出来,然后求最大公约数的话就是x,y之间可以可以有几个数被整除,这个理解的话也不难;首先我们先要了解公约数是什么,公约数是俩个数之间可以被共同整除的的数,而除以又是什么呢;我们可以想想看4/2,是不是把4分成俩份,4/1就是把4分成1份,4/4就是把4分成4份//所以是需要最大公约数,分成最多份数即点最多,但是不包括端点,再将公约数-1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int gys(int x,int y)//辗转相除法求公约数
{
int r;
while(y!=0)
{
r=x%y;
x=y;
y=r;
}
return x;
}
int main()
{
int x1,y1,x2,y2;
scanf("%d %d",&x1,&y1);
scanf("%d %d",&x2,&y2);
int x=abs(x1-x2);
int y=abs(y1-y2);
int t=gys(x,y)-1;
if(t>=0)printf("%d",t);
else printf("0");
}
Fence Repair
题目描述
农夫John要修理牧场的一段栅栏,他测量了栅栏,发现需要N(1<= N <= 20000)块木头,每块木头长度为Li(1<= Li <=50000)个单位。于是他购买了一条很长的、能锯成N块 的木头(即该木头长度是Li的总和)。农夫John忽略“损耗”,即忽略锯的时候产生锯末而造成的长度损耗。
农夫John没有锯子,于是他去找农夫Don,向他借锯子。
Don是一个守财奴,他不把锯子锯子借给John,而是要John为在木头上锯N-1次支付每一次锯的费用。锯一段木头要支付的费用等于这段木头的长度,即锯长度为21 的木头就要支付21美分。
例如,要将长度为21的木头锯成长度为8,5和8 的三段。
第一次锯木头花费为21,将木头锯成13和8;第2次锯木头花费13,将长度为13的木头锯成8和5;这样总的花费是21+13=34。如果将长度为21 的木头第一次锯成16和5的木头,则第二次锯木头要花费16,总的花费是37(大于34)。
Don让John决定在木头上切得次序和位置。请你帮助John确定锯N块木头索要花费的最少的钱。
输入
第1行: 一个整数N,表示木头的块数;
第2~N+1行: 每行给出一个整数,表示一段需要的木块长度。
输出
第1行: 一个整数,表示N-1次需要支付的最少费用。
样例输入 复制
3
8
5
8
样例输出 复制
34
思路:简单的贪心题;第一刀肯定是全长,后续切的话,我们切最大的一段,其他的为一段,这样剩下的刀就是最小的,我们可以发现这样是最优解,故我们先进行排序;先切大的,我们进行降序排序;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int cmp(const void *p1,const void *p2)//降序
{
return *(int *)p2-*(int *)p1;
}
int a[20002];
int main()
{
int n,sum=0,ans=0;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",a+i);sum+=a[i];
}
qsort(a,n,sizeof(a[0]),cmp);//降序排序
for(int i=0;i<n-1;i++)
{
ans+=sum;
sum-=a[i];
}
printf("%d",ans);
}
混合牛奶
题目描述
牛奶包装是一个如此低利润的生意,所以尽可能低的控制初级产品(牛奶)的价格变的十分重要。请帮助快乐的牛奶制造者(Merry Milk Makers)以可能的最廉价的方式取得他们所需的牛奶。快乐的牛奶制造公司从一些农民那购买牛奶,每个农民卖给牛奶制造公司的价格不一定相同。而且,如一只母牛一天只能生产一定量的牛奶,农民每一天只有一定量的牛奶可以卖。每天,快乐的牛奶制造者从每个农民那购买一定量的牛奶,少于或等于农民所能提供的最大值。给出快乐牛奶制造者的每日的牛奶需求,连同每个农民的可提供的牛奶量和每加仑的价格,请计算快乐的牛奶制造者所要付出钱的最小值。注意: 每天农民生产的牛奶的总数对快乐的牛奶制造者来说足够的。
输入
第 1 行:二个整数, N 和 M。第一个数值,N,(0<= N<=2,000,000)是快乐的牛奶制造者的一天需要牛奶的数量。第二个数值,M,(0<= M<=5,000)是他们可能从农民那买到的数目。第 2 到 M+1 行:每行二个整数,Pi 和 Ai。 Pi(0<= Pi<=1,000) 是农民 i 牛奶的价格。 Ai(0 <= Ai <= 2,000,000)是农民 i 一天能卖给快乐的牛奶制造者的牛奶数量。
输出
单独的一行包含单独的一个整数,表示快乐的牛奶制造者拿到所需的牛奶所要的最小费用
样例输入 复制
100 5
5 20
9 40
3 10
8 80
6 30
样例输出 复制
630
思路:分析题目可以发现是个贪心题(背包问题)
我们根据价格对其进行排序,得到可以用最少的前买最多牛奶的结果
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
//定义一个结构体便于排序
struct nn{
int jg;
int sl;
};
int main()
{
int m,n,i;
scanf("%d%d",&m,&n);
struct nn a[n];
for(i=0;i<n;i++)
{
scanf("%d%d",&a[i].jg,&a[i].sl);
}
struct nn temp;//用于交换
//选择排序
for( i=0;i<n-1;i++)
{
for(int j=i+1;j<n;j++)
{
if(a[j].jg<a[i].jg)//根据价格进行排序(升序)
{
temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}
}
long long sum=0,cnt=0;
for(i=0;i<n;i++)
{
if(cnt+a[i].sl<m)//保证买这个农民所有的牛奶的数量不会超过需要的数量
{
sum+=a[i].jg*a[i].sl;//sum用于求总价钱
cnt+=a[i].sl;//用于求已经加上的和
}
else break;//如果数量超过了就跳出循环
}
long long k=m-cnt;//(k是没有买满还差的个数)
sum+=k*a[i].jg;//(在加上差的数量的价格)
printf("%lld",sum);
}
/*
100 5
5 20
9 40
3 10
8 80
6 30
*/
最少硬币
题目描述
设有n 种不同面值的硬币,各硬币的面值存于数组T[1:n]中。现要用这些面值的硬币来找钱。可以使用的各种面值的硬币个数存于数组Coins[1:n]中。对任意钱数0≤m≤20001,设计一个用最少硬币找钱m的方法。
输入
输入的第一行中只有1 个整数给出n的值,第2 行起每行2 个数,分别是T[j]和Coins[j]。最后1 行是要找的钱数m。
输出
程序运行结束时,将计算出的最少硬币数输出。问题无解时输出-1。
样例输入 复制
3
1 3
2 3
5 3
18
样例输出 复制
5
提示
多组输入
思路:又是贪心,话不多说看代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int main()
{
int a[20005],c[20005],n,m,sum=0,cnt=0;
while(~scanf("%d",&n))
{
sum=0;cnt=0;
for(int i=0;i<n;i++)
{
scanf("%d%d",a+i,c+i);//分别为面值和个数
}
scanf("%d",&m);
for(int i=0;i<n-1;i++)
{
for(int j=i+1;j<n;j++)
{
if(a[i]<a[j])//从大到小排序
{
int temp=a[i];
a[i]=a[j];
a[j]=temp;
temp=c[i];
c[i]=c[j];
c[j]=temp;
}
}
}
for(int i=0;i<n;i++)//面值数组的遍历
{
for(int j=0;j<c[i];j++)//数量的遍历
{
if(sum+a[i]<=m)
{
sum+=a[i];
cnt++;//硬币数量++
}
}
}
if(sum==m)printf("%d\n",cnt);//如果刚好可以与面值相等就输出数量
else printf("-1");//无解的情况
}
}
/*
3
2 3
5 3
7 3
18
*/
cc学姐的烦恼
【题
题目描述
CC学姐作为一个优秀的人总会有很多事情烦恼她,现在就有一个事情令她很困恼,学校安排她去安排一系列
活动但是CC学姐有很多事情要忙,你能不能帮帮优秀的她呢?
输入
设有n个活动的集合E={1,2,…,n},其中每个活动都要求使用同一资源,如演讲会场等,而在同一时间内只有
一个活动能使用这一资源。每个活动i都有一个要求使用该资源的起始时间si和一个结束时间fi,且si <fi
。要求设计程序,使得安排的活动最多。
输入只有一个用例,第一行为一正整数n,表示活动个数,接下来n行,每行两个整数,分别表示第i号活动
的起始时间和结束时间。
输出
用一行输出所能安排的最多活动数。
样例输入 复制
11
5 9
0 6
12 14
8 12
1 4
3 5
5 7
3 8
6 10
8 11
2 13
样例输出 复制
4
思路思路:(贪心)但稍微复杂一点这一题的算法思路直接看代码,代码给注释
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
//结构体存入起始时间和结束时间
struct time {
int f;
int e;
};
int main()
{
time a[100];//此处的定义是c++中的,c语言采用struct time a[100];
int n,ans=1;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d%d",&a[i].f,&a[i].e);
}
//排序的过程
for(int i=0;i<n-1;i++)
{
for(int j=i+1;j<n;j++)
{
if(a[i].e>a[j].e)//降序排序(根据结束时间去排)(这里的话如果根据开始时间去排的话会导致结束时间会很大(有可能))
//但是我们根据结束时间排的话就可以保证结束时间递减,开始时间相应的小(虽然不是严格的从小到大)//自行理解理解
{
time temp=a[i];
a[i]=a[j];
a[j]=temp;
}
if(a[i].e==a[j].e)//如果结束时间相同则排起始时间
{
if(a[i].f>a[j].f)
{
time temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}
}
}
int flag=a[0].e;
for(int i=1;i<n;i++)
{
if(flag<=a[i].f)//如果前面的结束时间小于等于后面的开始时间,则说明可以排在一起
{
flag=a[i].e;//不要忘记把flag的值更新
ans++;
}
}
printf("%d",ans);
}
装箱问题
题目描述
有一个箱子容量为V(正整数,0<=V<=20000),同时有n个物品(0<n<=30),每个物品有一个体积(正整数)。
要求n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。
输入
第一行为一个整数,表示箱子容量;
第二行为一个整数,表示有n个物品;
接下来n行,每行一个整数表示这n个物品的各自体积。
输出
一个整数,表示箱子剩余空间。
样例输入 复制
24
6
8
3
12
7
9
7
样例输出 复制
0
思路:贪心贪心还是贪心(当然也可以用动态规划)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int cmp(const void*p1,const void *p2)//逆序
{
return *(int *)p2-*(int *)p1;
}
int main()
{
int a[31];
int n,m,sum1;
scanf("%d",&m);
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",a+i);
}
sum1=m;
qsort(a,n,sizeof(a[0]),cmp);//逆序排序
//这里的贪心经过证明是不完全的最优解,所以我们需要对逆序和正序都算一边,以求出最小
for(int i=n-1;i>=0;i--)
{
if(m-a[i]>=0)//正序
{
m-=a[i];
}
if(sum1-a[n-i-1]>=0)//逆序
{
sum1-=a[n-i-1];
}
}
if(sum1>m)printf("%d",m);//输出小的那个
else printf("%d",sum1);
}
背包问题(贪心)
题目描述
已知有一个可容纳重量为C的背包以及n件物品,其中第i件物品的重量为wi,每件物品的价值为pi(pi>0)。怎样向背包装如物品,才能使装入背包的物品的价值最大,编程求出最大价值(一件物品可以部分装入,部分装入是要合理、近似的装入,结果保留两位小数)。
输入
物品件数n
背包容量C
每件物品的重量和价值
输出
最大价值
样例输入 复制
3
30
20 40
15 25
15 25
样例输出 复制
56.66
思路:贪心就不再多说了,需要注意的是保留俩位小数不四舍五入的方法
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
struct bag
{
double w;//重量
double v;//价值
};
int main()
{
int n,l=0;
double c;
float sum=0;
struct bag a[100],temp;
scanf("%d%lf",&n,&c);//n件物品,c容量
sum=0;
for(int i=0;i<n;i++)
{
scanf("%lf%lf",&a[i].w,&a[i].v);//输入对应的种量和价值
}
for(int i=0;i<n-1;i++)
{
for(int j=i+1;j<n;j++)
{
if(a[i].v/a[i].w<a[j].v/a[j].w)//降序排列(依据为价值和数量的比值)//因为题目可以近似取,所以我们排序依据为比值
{
temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}
}
for(int i=0;i<n;i++)
{
if(c-a[i].w>=0)
{
c-=a[i].w;
sum+=a[i].v;
l=i;
}
}
if(c==0)printf("%.2f\n",sum);
else
{
sum+=c/a[l+1].w*a[l+1].v;
//以下步骤为防止取2位小数的时候四舍五入
int temp=(int)(sum*100);//先放大为100倍,此时为temp=5666
sum=temp/100.0;//再除以100;注意这里要是100.0或者强制转化temp为浮点型sum=(float)temp/100;
printf("%.2f\n",sum);
}
}
题目描述
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?输入
输入文件medic.in的第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。
输出
输出文件medic.out包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
样例输入 复制
70 3 71 100 69 1 1 2
样例输出 复制
3
提示
对于30%的数据,M <= 10;
对于全部的数据,M <= 100。
一道动态规划
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int max(int x,int y)
{
return x>y?x:y;
}
int main()
{
int T,M,t[101]={0},v[101]={0},dp[101][1001]={0};//dp数组用来存入最优解
scanf("%d %d",&T,&M);//时间和草药的数量,
for(int i=1;i<=M;i++)//一般来说都习惯从0开始,但是动态规划的话需要从1开始
{
scanf("%d%d",t+i,v+i);//输入时间和价值
}
for(int i=1;i<=M;i++)//遍历草药种类
{
for(int j=1;j<=T;j++)//时间的遍历(从0~T)
{
if(j<t[i])//遍历到的时间不够
{
dp[i][j]=dp[i-1][j];//最优解仍然是原来的那个
}
else//(时间足够)
{
dp[i][j]=max(dp[i-1][j],dp[i-1][j-t[i]]+v[i]);//采和不采(取其中大的)
}
}
}
printf("%d ",dp[M][T]);//输出最优解
}
最大的x-y
题目描述
给定一个长度为n的序列a,并且序列a中的元素只由1或0构成
你可以执行一种操作,选定序列a中的一个元素a[i],使得a[i]=1-a[i]
最后你可以得到一个x和一个y
x:序列a中连续1的最长序列长度
y:操作次数
要求你求出最大的x-y
输入
第一行给定一个t(1<=t<=1e3),表示有t组样例
对于每一组样例,总共有两行
第一行给定一个n(1<=n<=1e5),表示序列a的长度
第二行给定n个元素,表示序列a
输出
最大的x-y
样例输入 复制
2
3
1 0 1
5
0 1 0 1 0
样例输出 复制
2
2
提示
对于第一组样例,我们可以对a[2]执行操作,序列a修改为{1 1 1},x=3,y=1 最终x-y=2
对于第二组样例,我们可以对a[3]执行操作,序列a修改为{0 1 1 1 0},x=3,y=1 最终x-y=2
思路:这题的话我们要发现一个规律,那就是我们改变一个0,那么1多一个,改变次数也+1;所以其实是抵消了的,所以我们把所有的0都改变为1的情况下其实就是最大值,所以我们只需要把0的个数求解出来然后和总数做差。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int a[100001];
int main()
{
int t,n,cnt=0;
scanf("%d",&t);
for(int j=0;j<t;j++)
{
scanf("%d",&n);cnt=0;
for(int i=0;i<n;i++)
{
scanf("%d",a+i);
if(a[i]==0)cnt++;//计算其中0的个数
}
printf("%d\n",n-cnt);//用总数减去0的个数就是最优解
}
}
题目六
题目描述
给定一个长度为n的序列a,选出其中连续且非空的一段使得这一段的和最大。
输入
第一行输入一个整数n (n < 1e5)
第二行输入n个整数a[i] (-1e4 < a[i] < 1e4)
输出
输出这个序列中最大的子段和。
样例输入 复制
7
2 -4 3 -1 2 -4 3
样例输出 复制
4
思路:这题初看和我们之前做过的最大字段和简直是一模一样,
唯一的区别就是,这里并没有说明全部为负数的时候输出什么,既然是最大,那么就对负数进行特判,求出所有负数的最大值自然是最大字段和
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int a[100005];
int main() {
int n;
while (~scanf("%d", &n))
{
int flag = 1;
for (int i = 0; i < n; i++)
{
scanf("%d", a + i);
if (a[i] > 0)flag = 0;//特判全为负数的标志
}
int max = 0, temp = 0;
//此时存在整数//求法和最大子段和一样
if (flag == 0)
{
for (int i = 0; i < n; i++)
{
temp += a[i];
if (temp > max) {
max = temp;
}
if (temp <= 0) {
temp = 0;
}
}
printf("%d\n", max);
}
//全部是负数的情况下
else
{
int m = a[0];//打擂台求出最大值即为最大子段和
for (int i = 1; i < n; i++)
{
if (m < a[i])m = a[i];
}
printf("%d\n", m);
}
}
}