问题总结

问题总结

1.if-else问题

如果下面这个else if改成if,那么就会变成后面两个if-else配对,那么最左侧特判结束后还会进入这个判断导致边界溢出出错
在这里插入图片描述

2.STL中erase导致指针失效的问题

删除不是n的元素,注意删除后it的位置被破坏不能++操作,我们在删除前要先保存
​ for (auto it=pos.begin(); it!=pos.end();it++)
​ if (ans[*it].dp != n)
​ {
​ auto temp = it;
​ pos.erase(it); //预先缓存
​ it = temp;
​ }

3.数组初始化

在数组中,初始化数量如果小于数组大小,剩下的用0填充,所以如果想初始化成0,直接int a[10]={0}就行,但是如果想初始化为其他值,必须用memset,头文件<string.h>,但是还是建议用fill.。

fill函数:

  • 按照单元赋值,将一个区间的元素都赋同一个值
  • 在头文件<algorithm>里面
  • fill()函数参数:fill(first,last,val);
    first 为容器的首迭代器,last为容器的末迭代器,val为将要替换的值。

1 用fill函数给一维赋值:

int num[maxn];
fill(num,num+maxn,-1);
或者fill(num,num+n,-1);指定数组长度赋值12

2 用fill给二维数组赋值:

int num[maxn][maxn];
fill(num[0],num[0]+maxn*maxn,-1);
//值得注意的是,给二维数组赋值时,首地址必须写num[0]。

4.分割平面、空间问题 数学公式

(1) n条直线最多分平面问题

题目:n条直线,最多可以把平面分为多少个区域。

公式:f(n)=n(n+1)/2+1

(2)折线分平面

公式:f(n)=2n^2-n+1

(3)封闭曲线分平面

公式:f(n)=n^2-n+2

(4)平面分割空间问题

公式:f(n)=(n^3+5n)/6+1

推导过程:

(1) n条直线最多分平面问题

题目描述:

n条直线,最多可以把平面分为多少个区域。

分析:

可能你以前就见过这题目,这充其量是一道初中的思考题。但一个类型的题目还是从简单的入手,才容易发现规律。

当有n-1条直线时,平面最多被分成了f(n-1)个区域。则第n条直线要是切成的区域数最多,就必须与每条直线相交且不能有同一交点。 这样就会得到n-1个交点。这些交点将第n条直线分为2条射线和n-2条线段。而每条射线和线断将以有的区域一分为二。这样就多出了2+(n-2)个区域。

故:

f(n)=f(n-1)+n
=f(n-2)+(n-1)+n
……
=f(1)+1+2+……+n
=n(n+1)/2+1

(2) 折线分平面(hdu2050)
根据直线分平面可知,由交点决定了射线和线段的条数,进而决定了新增的区域数。当n-1条折线时,区域数为f(n-1)。为了使增加的区域最多,则折线的两边的线段要和n-1条折线的边,即2*(n-1)条线段相交。那么新增的线段数为4*(n-1),射线数为2。但要注意的是,折线本身相邻的两线段只能增加一个区域。
故:

f(n)=f(n-1)+4(n-1)+2-1
=f(n-1)+4(n-1)+1
=f(n-2)+4(n-2)+4(n-1)+2
……
=f(1)+4+4*2+……+4(n-1)+(n-1)
=2n^2-n+1

//P0204 平面分隔线
#include<stdio.h>
int C, n;
//要达到最大分隔数,每新增一条折线都要使其两条边与之前C-1条折线的两个边都相交
//对于第n-1个折线的情况,有2*(n-1)条线,新增的每条线,与这每条线都相交,
//多出2*(n-1)-1+1条线段,1条射线。因为新增的线有两条(折线),乘以2,就是4(n-1)条线段,2条射线
//本来新增的每条射线和线段都可以将其平面分成两块,但是由于折线那个∠角的两个新增线段是连接的,所以两者只能算新增一个平面,要减一
int main(void)
{
	long long int dp[10001];  //I条折线的分隔平面数
	dp[1] = 2;   //一条折线最多分2块
	dp[2] = 7;
	for (int i = 3; i <= 10000; i++)  
		dp[i] = dp[i - 1] + 4 * (i - 1) + 2 - 1;
	scanf("%d", &C);
	for (int i = 0; i < C; i++)
	{
		scanf("%d", &n);
		printf("%lld\n", dp[n]);
	}
	return 0;
}

5.STL中的元素和实际元素不是一个东西

在STL中,一般对容器的内存分配和构造是分开的2个过程,STL有专门的空间配置器负责分配内存,而构造则是通过placement new在已申请的内存上进行的,vector也不除外,下面是vector的push_back函数源码:

template <class T, class Alloc = alloc>
void vector::push_back(const T& x)
{
	if (finish != end_of_storage)
	{
		construct(finish, x);
		++finish;
	}
	else
	{
		insert_aux(finish, x);
	}
}

//其中,construct是STL的全局函数,是所有容器共用的,它的具体实现如下:

template<class T1, class T2>
inline void construct(T1* p, const T2& value)
{
	new(p) T1(value);    //placement new, 调用T1::T1(value);
}

//而insert_aux是vector自己的成员函数,具体实现如下:

template <class T, class Alloc = alloc>
void vector<T, Alloc>::insert_aux(iterator position, const T& x)
{
	//还有备用空间
	if (finish != end_of_storage)
	{
		//在备用空间起始处构造一个元素,并以vector最后一个值为其初值
		construct(finish, *(finish - 1));
		//调整水位
		++finish;
		//拷贝一个元素
		T copy_x = x;
		//把vector插入位置position之后的元素往后移一位
		copy_backward(position, finish - 2, finish - 1);
		//给position指向的地方赋值,赋值内容为前面拷贝的元素
		*position = copy_x;
	}
	//已无备用空间
	else
	{
		const size_type old_size = size();
		//如果原来的vector为空,则申请一个元素的空间,否则申请可以容纳原来2倍元素的空间
		const size_type len = old_size == 0 ? 1 : 2 * old_size;
		//全局函数,申请空间
		iterator new_start = data_allocator::allocate(len);
		iterator new_finish = new_start;

		try
		{
			//将原来vector的position之前的内容拷贝到新的vector前面
			new_finish = uninitialized_copy(start, position, new_start);
			//调用构造函数为新插入的元素赋值
			construct(new_finish, x);
			//调整水位
			++new_finish;
			//将原来vector的postion之后的内容拷贝到新的vector后面
			new_finish = uninitialized_copy(position, finish, new_finish);
		}
		catch (...)
		{
			//析构
			destroy(new_start, new_finish);
			//释放刚刚申请的内存
			data_allocator::deallocate(new_start, len);
			throw;
		}
	
		//析构原vecotr
		destroy(begin(), end());
		//释放原vector的空间
		deallocate();
	
		//调整迭代器指向新的vector
		start = new_start;
		finish = new_finish;
		end_of_storage = new_start + len;
	}
}

所以说,在平时的算法使用队列queue,vector的过程中,对stl容器中的元素的改变影响不到原本的元素。所以我们向例如一个存放node结构体的vector添加数据时,不用每次都新建一个结构体,只用新建一个结构体,更改结构体的数据域,因为push_back执行时会申请新的空间,拷贝。例如下面的例子:

vector<node> v;
node user;
user.id = 1;
v.push_back(user);
user.id = 2;
v.push_back(user);
for (int i = 0; i < v.size(); i++)
	printf("%d\n", v[i].id);

6.定义优先队列的顺序

在结构体中自定义内嵌比较函数,因为优先队列是默认从大到小,如果如下写的话,优先级小的是r大的,优先级高的是r小的,也就是优先队列队首是r最小的。当然,这样写的话可以直接使用sort函数,按照r从大到小排序(sort默认从小到大)

struct node
{
    int l,r;
    bool operator < (const node &a) const{
        return r > a.r;
    }
}a[maxn];

7.计算组合数Cmn

void Combine(int left, int right, int k, vector<vector<int> >& res, vector<int>& temp)
{
	if (right - left + 1 < k) return;
	if (k == 0)
	{
		res.push_back(temp);
		return;
	}
	//对于left-right之间的元素,有选和不选两种情况
	temp.push_back(left);
	Combine(left + 1, right, k - 1, res, temp);  //选left元素
	temp.pop_back();
	Combine(left + 1, right, k, res, temp);  //不选left元素
}
class Solution {
public:
	vector<vector<int> > combine(int n, int k) {
		vector<vector<int> >res;
		vector<int>tmpres;
		helper(1, n, k, tmpres, res);
		return res;
	}
	//从[left, right]范围内选取k个数,tmpres是一个临时组合
	void helper(int left, int right, int k, vector<int>& tmpres, vector<vector<int> >& res)
	{
		if (k == 0)
		{
			res.push_back(tmpres);
			return;
		}
		for (int i = left; i <= right - k + 1; i++)
		{
			tmpres.push_back(i);
			helper(i + 1, right, k - 1, tmpres, res);
			tmpres.pop_back();
		}
	}
};

To be continued…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值