Archive for ‘边走编程’ Category

May 4, 2010

  有码有真相,各位看官您上眼:

1
2
3
4
5
6
int main()
{
	bar(++foo(372)); //~ 此行报错
	bar(++foo(wrap_int(372))); //~ 无错
	return 0;
}

  第一个bar调用中foo返回int型临时对象,想要把这个int临时对象++后传给bar,未遂。第二个bar调用中,foo返回一个包装过的int, 即wrap_int,对这个临时对象进行++(重载),得逞。结论:C++中,基本类型(内置类型)的临时对象不可以作为左值(l-value),即不可以修改;用户自定义类型的临时对象可以作为左值。

Tags: . 41 views
April 30, 2010

  现有一个数列S = \{1,2,3,\ldots,n\},另有一个栈Stack和一个队列Queue,Stack与Queue初始为空。现对S中元素依次进行如下操作:

  1. 若Stack为空,则从S中取出一个元素入栈;
  2. 若Stack非空,则有两种选择:将栈顶元素弹出并入队,或者从S中取出一个元素入栈;
  3. 若S元素已经取完则操作结束,否则执行操作1或2。

  问最终队列Queue有多少种排列情况?聪明且见识广博的你或许一下子就可以说出答案:\120dpi \color{red}\frac{C_{2n}^{n}}{n+1}\quad\textbf{or}\quad\frac{\binom{2n}{n}}{n+1}
  现在对这个结果进行证明。
  设1表示入栈操作,0表示出栈操作。那么上面对n个元素的入栈和出栈操作就构成了长度为2n的由0和1组成的序列,其中1和0的个数均为n个,在没有任何限制的情况下,一共有C_{2n}^n个这样的序列。但是,这里的01序列是建立在一些列的入栈出栈操作的基础上的,因此就会受到入栈、出栈操作的限制。这里,唯一的限制就是栈为空时,无法进行出栈操作。反映到这个01序列中就是,任意位置之前,0的个数都能比1的个数多。因为有了01序列的总数C_{2n}^n,所以为了找出满足条件的序列的个数,只需要找出不符合条件的序列的个数。

Tags: . 68 views
April 29, 2010

  提到C++ STL,首先被人想到的是它的三大组件:Containers, Iterators, Algorithms,即容器,迭代器和算法。容器为用户提供了常用的数据结构,算法大多是独立于容器的常用的基本算法,迭代器是由容器提供的一种接口,算法通过迭代器来操控容器。接下来要介绍的是另外的一种组件,函数对象(Function Object,JJHou译作Functor仿函数)。

什么是函数对象

  顾名思义,函数对象首先是一个对象,即某个类的实例。其次,函数对象的行为和函数一致,即是说可以像调用函数一样来使用函数对象,如参数传递、返回值等。这种行为是通过重载类的()操作符来实现的,举例说明之,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Print
{
public:
    void operator()(int n)
    {
        std::cout<<n<<std::endl;
        return ;
    }
};
int
main(int argc, char **argv)
{
    Print print;
    print(372);
    print.operator()(372); //~ 显式调用
    return 0;
}
Tags: ,. 211 views
April 26, 2010

进程之死

  对于一个用C++写的程序,被加载至内存后运行,最终走向死亡。程序的死亡大致有三种:

  • 自然死亡,即无疾而终,通常就是main()中的一个return 0;
  • 自杀,当程序发现自己再活下去已经没有任何意义时,通常会选择自杀。当然,这种自杀也是一种请求式的自杀,即请求OS将自己毙掉。有两种方式:void exit(int status)和void abort(void)。
  • 他杀,同现实不同的是,程序家族中的他杀行径往往是由自己至亲完成的,通常这个至亲就是他的生身父亲(还是母亲?)。C++并没有提供他杀的凶器,这些凶器往往是由OS直接或者间接(通过一些进程库,如pthread)提供的。

  自然死是最完美的结局,他杀是我们最不愿意看到的,自杀虽是迫不得已,但主动权毕竟还是由程序自己掌控的。下面探究程序一下不同的死亡方式对对象的析构有何影响。

Tags: ,. 154 views
April 20, 2010

Dijkstra算法简述

  Dijkstra算法是图论中的一种求单源最短路的算法,即从一个点开始到所有其他点的最短路。其基本原理是:集合T包含所有当前已经找到最短路径的点,初始情况下T中只有源点。U维护当前源点通过T中各点到达其他各点的距离。从源点开始,每次新扩展一个属于U、距离T中点最短的点,用该点更新U中的与其相邻的点的距离。当所有边权都为正时,由于不会存在一个距离更短的没扩展过的点,所以这个点的距离永远不会再被改变,因而保证了算法的正确性。不过根据这个原理,用Dijkstra求最短路的图不能有负权边,因为扩展到负权边的时候会产生更短的距离,有可能就破坏了已经更新的点距离不会改变的性质。若要处理含有负权值边的情况,就需要使用Bellman-Ford算法了。

Dijkstra算法流程

  在以下说明中,start为源,Adj[u,v]为点u和v之间的边的长度,结果保存在Closest[]

  • 初始化:源的距离Closest[start]设为0,其他的点距离设为无穷大,同时把所有的点的状态设为没有扩展过。
  • 以下过程循环n-1次:
    在没有扩展过的点中取一距离最小的点u,并将其状态设为已扩展。
    对于每个与u相邻的点v,如果Closest[u]+Adj[u,v] < Closest[v],那么把Closest[v]更新成更短的距离Closest[u]+Adj[u,v]。此时到点v的最短路径上,前一个节点即为u。
  • 结束。此时对于任意的u,Closest[u]就是start到u的距离。
Tags: ,. 201 views

  在现行的C++标准中有这么一个名字,std,它是一个namespace(命名空间)。C++标准库的所有类型/对象/函数/模板都定义在这个命名空间中。关于”What namespace? Why namespace? How namespace?” 的问题,任何一本基础的C++读物中都会有介绍,此处略过。为了使用std内定义的对象,可供使用的语法无非是

1
2
3
4
5
6
7
std::vector<int> ivec;
//~ or
using std::vector;
vector<int> ivec;
//~ or
using namespace std;
vector<int> ivec;

  现在有这样一种需要,我想为std内的某种类型(比如string)重载操作符,而不想使用std内为我们提供的版本。

Tags: . 27 views
April 19, 2010

  一个无向、加权、连通图的生成树是指这样一棵树,它包含该图所有的n个顶点,和该图边集E中的n-1个边。最小生成树就是该图所有生成树中各边权值之和(构造代价)最小的生成树。求指定图的最小生成树有多种算法,除了这里要介绍的Kruskal算法,还有Prim算法、Sollin算法等。

Kruskal算法描述

  用Kruskal算法为无向加权连通图G构造最小生成树T的步骤如下:首先初始化T为一个包含所有n个顶点、0个边的森林,E为G的边集。每次从E中取出一条具有最小权值的边(并从E中删除改变),如果该边不与T中其他边构成回路,则将该边加入T,否则舍弃该边。重复上述操作至T中含有n-1条边,此时T即Kruskal算法构造出的最小生成树。

Kruskal算法证明

  易证,对于一个无向加权连通图,总是存在一棵或以上的有限课生成树,而这些生成树中肯定存在至少一棵最小生成树。下面证明Kruskal算法构造的生成树是这些最小生成树中的一棵。
  设T为Kruskal算法构造出的生成树,U是G的最小生成树。如果T==U那么证明结束。如果T != U,我们就需要证明T和U的构造代价相同。由于T != U,所以一定存在k > 0条边存在于T中,却不在U中。接下来,我们做k次变换,每次从T中取出一条不在U中的边放入U,然后删除U一条不在T中的边,最后使T和U的边集相同。每次变换中,把T中的一条边e加入U,同时删除U中的一条边f。e、f按如下规则选取:a). e是在T中却不在U中的边的最小的一条边;b). e加入U后,肯定构成唯一的一个环路,令f是这个环路中的一条边,但不在T中。f一定存在,因为T中没有环路。

Tags: ,. 417 views
April 18, 2010

  今天在VS中遇到一个十分诡异的事情,先看下面这个简单的程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class cmp
{
public:
	bool operator()(long long l, long long r)
	{
		//~ 此间内容可略去不看。
		//~ 如果l和r含有的digits完全相同则返回false,
		//~ 如125 vs. 512
		char tmp1[1024], tmp2[1024];
		sprintf(tmp1,"%ld",l);
		sprintf(tmp2,"%ld",r);
		sort(tmp1, tmp1+strlen(tmp1));
		sort(tmp2, tmp2+strlen(tmp2));
		if (!strcmp(tmp1, tmp2))
		{
			return false;
		}
		return true;
	}
};
Tags: . 20 views
April 14, 2010

  设”此物”为x,则x满足同余方程组:
\left\{\begin{array}{c}x\equiv 2 (\mod 3)\\x\equiv 3 (\mod 5) \\x\equiv 2 (\mod 7)\end{array}\right.
  若存在x满足上述方程组,则x + k*3*5*7也满足该方程其中k任意整数,方程的最小正整数解满足x\mod{3*5*7},即对3,5,7的最小公倍数求余。
  在介绍求x的具体方法之前,先介绍两个简单的定理(证明从略):

  1. 被除数增加(或减少)除数的倍数,除数不变,则余数电不变;
  2. 被除数扩大(或缩小)指定的倍数,除数不变,则余数扩大(或缩小)同样的倍数(余数总小于除数)。
Tags: ,. 48 views
April 10, 2010

  首先,你知道下面的语句的语义是什么吗?

int (a);

或许写成这样你会更明白一点:

int (a) = 0;

它等同于

int a = 0;
Tags: . 28 views
Page 2 of 1512345678910...Last »