问题总结
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…