蓝桥杯训练基础知识之memset,typedef(18年记)

memset

在前面不止一次说过,定义变量时一定要进行初始化,尤其是数组和结构体这种占用内存大的数据结构。在使用数组的时候经常因为没有初始化而产生“烫烫烫烫烫烫”这样的野值,俗称“乱码”。

每种类型的变量都有各自的初始化方法,memset() 函数可以说是初始化内存的“万能函数”,通常为新申请的内存进行初始化工作。它是直接操作内存空间,mem即“内存”(memory)的意思。该函数的原型为:

    # include <string.h>
    void *memset(void *s, int c, unsigned long n);

函数的功能是:将指针变量 s 所指向的前 n 字节的内存单元用一个“整数” c 替换,注意 c 是 int 型。s 是 void* 型的指针变量,所以它可以为任何类型的数据进行初始化。

memset() 的作用是在一段内存块中填充某个给定的值。因为它只能填充一个值,所以该函数的初始化为原始初始化,无法将变量初始化为程序中需要的数据。用memset初始化完后,后面程序中再向该内存空间中存放需要的数据。

memset 一般使用“0”初始化内存单元,而且通常是给数组或结构体进行初始化。一般的变量如 char、int、float、double 等类型的变量直接初始化即可,没有必要用 memset。如果用 memset 的话反而显得麻烦。

当然,数组也可以直接进行初始化,但 memset 是对较大的数组或结构体进行清零初始化的最快方法,因为它是直接对内存进行操作的。

这时有人会问:“字符串数组不是最好用’\0’进行初始化吗?那么可以用 memset 给字符串数组进行初始化吗?也就是说参数 c 可以赋值为’\0’吗?”

可以的。虽然参数 c 要求是一个整数,但是整型和字符型是互通的。但是赋值为 ‘\0’ 和 0 是等价的,因为字符 ‘\0’ 在内存中就是 0。所以在 memset 中初始化为 0 也具有结束标志符 ‘\0’ 的作用,所以通常我们就写“0”。

memset 函数的第三个参数 n 的值一般用 sizeof() 获取,这样比较专业。注意,如果是对指针变量所指向的内存单元进行清零初始化,那么一定要先对这个指针变量进行初始化,即一定要先让它指向某个有效的地址。而且用memset给指针变量如p所指向的内存单元进行初始化时,n 千万别写成 sizeof( p),这是新手经常会犯的错误。因为 p 是指针变量,不管 p 指向什么类型的变量,sizeof( p) 的值都是 4。
下面写一个程序:

    # include <stdio.h>
    # include <string.h>
    int main(void)
    {
        int i;  //循环变量
        char str[10];
        char *p = str;
        memset(str, 0, sizeof(str));  //只能写sizeof(str), 不能写sizeof(p)
        for (i=0; i<10; ++i)
        {
            printf("%d\x20", str[i]);
        }
        printf("\n");
        return 0;
    }

根据memset函数的不同,输出结果也不同,分为以下几种情况:
m
emset(p, 0, sizeof( p)); //地址的大小都是4字节
0 0 0 0 -52 -52 -52 -52 -52 -52

memset(p, 0, sizeof(*p)); //*p表示的是一个字符变量, 只有一字节
0 -52 -52 -52 -52 -52 -52 -52 -52 -52

memset(p, 0, sizeof(str));
0 0 0 0 0 0 0 0 0 0

memset(str, 0, sizeof(str));
0 0 0 0 0 0 0 0 0 0

memset(p, 0, 10); //直接写10也行, 但不专业
0 0 0 0 0 0 0 0 0 0

总结:memset 是一个初始化函数对于 数组和结构体来说 相当快速
因为是直接对内存进行操作 三个参数分别是(初始化量的引用或者指针 ,所替换的数(一般都用0或’\0’),需要替换的数的数量(一般都是用初始化量的引用的sizeof))

=======================================================================================================

01背包问题 dp 动态规划

(暂时还没有理解清楚)
在这里插入图片描述
分析: 深度搜索算法: 先简单来看一下深搜的算法吧。这题的深搜就像一棵二叉树,从每个节点向下都会有两个分支,一个是加a,一个是减b。需要考虑的是根节点的大小,也就是第一个数的大小。第一个数没有明确告诉那么必然是在一个区间内浮动无法确定,需要一个个测试是否符合要求然后确定。注意到输入的数据有4个,第一个数的大小的区间可以用这四个数表示出来,可以确定最大值和最小值。最大值为s+nb,最小值为s-na。对这个区间内的每个整数做一遍深搜,统计满足条件的值可得答案。

代码:

public class Main{

static int num;//统计方案数 
static int n;//数列长度 
static int s;//和 
static int a;//加a 
static int b;//减b 
public static void main(String[] args)
 { 
		Scanner scanner=new Scanner(System.in); 
		n=scanner.nextInt(); 
		s=scanner.nextInt(); 
		a=scanner.nextInt(); 
		b=scanner.nextInt(); 
		int total=s+n*b;//第一个数可能取得的最大值 
		for (int i = s-n*a; i <=total ; i++) 
		{ 
			dfn(i,i,1);//对每个符合要求的数列的首项做一次深搜
		} 
		 System.out.PRintln(num); 
 } //cur是数列当前项的值,all是数列累计值的总和,len是数列的长度 
 private static void dfn(int cur,int all,int len)
 { 
		if(len==n)
	   { 
	   		if(all==s)
		    { num++;
		     num=num%100000007; 
		     return ;
		    }
		     else return;
	      //缺少此项堆栈溢出 
	    }
       dfn(cur+a,all+cur+a,len+1); //下一项加a的情况
       dfn(cur-b,all+cur-b,len+1); //下一项减b的情况 
       } 
  }

可惜出现运行超时的情况

动态规划算法: 简单介绍深搜后重点来了,这题用动态规划(dp)解才是正解。在讲这题之前首先要回顾一下01背包问题中统计方案数的问题。

有容量为c的背包要装入体积为1~n的物品,每种物品各一个,求恰好将背包装满的方案数。

我们用二维数组Bo[i][j]来存储背包容量递增,物品按体积1n的顺序递增时方案数的情况。i表示有体积为0i的物品各一个,j表示背包的容量为j。首先要初始化当已有的物品体积数量为0,也就是相当于只有一个体积为0的物品时,背包容量j为0的方案数为1个,而容量大于0的方案数全为0。
之后来看关键的状态转移方程:
Bo[i][j]=Bo[i-1][j] ; i>j
Bo[i][j]=Bo[i-1][j]+Bo[i-1][j-i] ; i<=j
二维数组是从上到下,从左到右进行计算的,每个元素都与之前已经计算过元素相关。
第i行j列的元素,如果i>j,也就是新添加的可选物品的体积大于背包的容积,无法放入背包,所以新添加的可选物品不能够增加方案数。
如果i<=j,新添加的可选物品的体积小于背包的容积,有可能与其他物品组合装入背包,这时方案数为当没有添加入i体积物品时背包容量为j的方案数Bo[i-1][j](装入后背包内体积还是小于背包体积),加上当没有添加入i体积物品时但背包容量恰好可以装入i体积物品的方案数Bo[i][j]=Bo[i-1][j]+Bo[i-1][j-i](装入后背包内体积等于背包体积)。这样逐行计算即可知道所有情况下的方案数。 但是背包问题和本题有何关系呢,似乎毫不相干?

这题的难点就在于如何将本题转化为01背包问题。 设数列首项为t,F(i)={a,-b},第i项加上F(i)为第i+1项。所以第2项可表示为t+F(1),第2项可表示为t+F(1)+F(2),第3项可表示为t+F(1)+F(2)+F(3)…………把第一项到第n项累加起来可得表达式nt + (n-1)F(1) + (n-2)F(2) + ……. +F(n-1) =s。nt = s - {(n-1)F(1) + (n-2)F(2) + ……. +F(n-1)}。由于F(i)不为a就为-b,设有c个a,F(i)的系数相加共有1+2+……+n-1 = (n-1)*n/2项目,所以共有c-(n-1)n/2个b。所以令temp = s - ca + ((n-1)*n/2 - c)b,当temp%n = = 0时,说明temp == nt,满足要求。并且,c为1n-1个数中的若干个数组合相加而得的,每一种组合都是一种方案,到此,我们就可以发现这题已经转化为求01背包问题的方案数了。即从体积为1n-1的物品中有几种方案能够恰好填满背包。

i j 0 1 2 3 4 5 6 7 8 9 0 1 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 2 1 1 1 1 0 0 0 0 0 0 3 1 1 1 2 1 1 1 0 0 0 上图为测试输入样例的动态规划的结果,每一项都是(i,j)条件下的方案数,c的可能取值为4的倍数,该输入中有0和4符合要求,所以将(3,0)和(3.4)的值相加即为答案2。

还要注意到时间和空间的节省。时间上因为1~i-1体积的物品加起来最多也只能刚好填满i*(i+1)/2容量的背包,所以大于背包容量大于i*(i+1)/2体积的不用计算,固定初始化为0。空间上也不需要开辟上图那么大的空间,用滚动数组只需开辟两行即可,因为(i,j)的值只与i-1行有关,最终结果也只和最后求得的一行有关,所以只需存储当前行与上一行。

==========================================================================================================================================
1.按照题目要求,最终得到的序列的长度为n,和为s,并且后一项是前一项加a或减b,我们不妨将这个操作封装在一起,记作P操作,即P=(a,-b)。
2.设首项为x,可以得到一个等式x+(x+P)+(x+2P)+…+(x+(n-1)P)=s,将这个式子整理一下,就是nx+P+2P+…+(n-1)P=s,即(s-(P+2P+…+(n-1)P))/n=x。
3.由题意,x肯定是一个整数,并且由于每一个P代表一个a或者一个-b,所以a和b的总数为n*(n-1)/2,也就是说只要确定了a的个数,那么b的个数也就确定了。
4.关键问题是对于一个确定的a的个数,方案不只有一种,而且a的个数肯定是由(1,2,3,…,n-1)这其中的若干项组成的,,我们把这些项看作元素,第i个元素的权值为i于是,下面就开始构造递推方程5.首先,定义一个数组dp[i][j],表示前i个元素组成和为j的序列的方案数,这里的和j表示的是所有的a的和,很明显当i!=0时dp[i][0]=1,当j!=0时dp[0][j]=0,然后我们要分两种情况讨论(1)、i>j时,因为每一个元素i权值都是i,所以当元素的个数大于和的时候,第i个元素的权值已经超过了和,所以第i个元素绝对不能使用,即dp[i][j]=dp[i-1][j]。(2)、i<=j时,第i个元素的权值是小于等于和的,所以可以用,也可以不用,如果不用,那么就是dp[i-1][j],如果用,就是dp[i-1][j-i],这个有点类似于01背包,所以dp[i][j]=dp[i-1][j]+dp[i-1][j-i]。OK,通过上面的分析,我们得到了递推方程,但还有一个问题,就是空间的问题,题目给出的i的最大值达到1000,相应的j也就是1000^2,我们是不可能开出这么大的数组的,观察递推方程,我们可以看出下一个状态只和前一个状态有关,而且我们实际上只需要最后一个状态即,dp[j],于是可以使用滚动数组。


#include<stdio.h>
#include<string.h>
#include<algorithm>
#define INF 0X3f3f3f3f
#define IN __int64
#define ull unsigned long long
#define ll long long
#define N 1010
#define M 100000007
using namespace std;
ll n,s,a,b;
int c[2][N*N];
int g;
int cnt;
void DP()
{
	int i,j;
	g=0;
	memset(c,0,sizeof(c));
	c[g][0]=1;
	for(i=1;i<n;i++)
	{
		g=1-g;
		for(j=0;j<=i*(i+1)/2;j++)
		{
			if(i>j)
				c[g][j]=c[1-g][j];
			else
				c[g][j]=(c[1-g][j]+c[1-g][j-i])%M;
		}
	}
}
int main()
{
	while(scanf("%lld%lld%lld%lld",&n,&s,&a,&b)!=EOF)
	{
		cnt=0;
		DP();
		ll i,t;
		for(i=0;i<=n*(n-1)/2;i++)
		{
			t=s-i*a+(n*(n-1)/2-i)*b;
			if(t%n==0)
				cnt=(cnt+c[g][i])%M;
		} 
		printf("%d\n",cnt);
	}
	return 0;
	
}

==============================================================================================

typedef的用法

https://blog.csdn.net/wangqiulin123456/article/details/8284939

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

黑瞳丶

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

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

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

打赏作者

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

抵扣说明:

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

余额充值