第十五周总结

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);
		}
	}
}

 

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卡卡卡卡罗特

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值