Mar
8
抽象题意:给出最多200,000个不超过1000的非负整数,对最多200,000次以下两类操作进行处理
1. M x y ——求第x到第y个整数之间的所有整数的和。
2. S x v ——将第x个整数的值置为v。
显然,硬搞要TLE的,稍微想了一下,就知道应该用线段树(或者树状数组?)来完成。
关于线段树可以参考 http://acm.whu.edu.cn/blog/read.php?26 以及 http://acm.whu.edu.cn/blog/read.php?51
简单的说,就是用一个平衡二叉树来存储这些东西,
每个叶节点存放一个整数(一个单位"线段"),
而每个分支节点存放的是一条"线段"
在M操作的时候,只要递归地将线段xy分割,能够在logN的时间内查询出
在S操作的时候,只要递归地找到对应的叶节点更新,并在返回的时候一层层更新涉及到的分支节点即可,也是logN。
好久没有写稍长的代码了,这个线段树的具体实现也忘得差不多了,还是去翻了一下yyt的论文。
不过这次写得顺利多了,思路比较清晰。
具体的代码为
1. M x y ——求第x到第y个整数之间的所有整数的和。
2. S x v ——将第x个整数的值置为v。
显然,硬搞要TLE的,稍微想了一下,就知道应该用线段树(或者树状数组?)来完成。
关于线段树可以参考 http://acm.whu.edu.cn/blog/read.php?26 以及 http://acm.whu.edu.cn/blog/read.php?51
简单的说,就是用一个平衡二叉树来存储这些东西,
每个叶节点存放一个整数(一个单位"线段"),
而每个分支节点存放的是一条"线段"
在M操作的时候,只要递归地将线段xy分割,能够在logN的时间内查询出
在S操作的时候,只要递归地找到对应的叶节点更新,并在返回的时候一层层更新涉及到的分支节点即可,也是logN。
好久没有写稍长的代码了,这个线段树的具体实现也忘得差不多了,还是去翻了一下yyt的论文。
不过这次写得顺利多了,思路比较清晰。
具体的代码为
Feb
25
这篇看起来忒有意思,回头有空了我翻译一下。
How To Read C Declarations
zz from http://blog.chinaunix.net/u1/36290/showart_443799.html
Even experienced C programmers have difficulty reading declarations that go beyond simple arrays and pointers. For example, is the following an array of pointers or a pointer to an array?
What the heck does the following mean?
Naturally, it's a pointer to an array of pointers to functions returning integers. ;)
This short article tells you how to read any C declaration correctly using a very simple technique. I am 99% certain that I read this in a book in the late 1980s, but I can't remember where. I doubt that I discovered this on my own (even though I've always been delighted by computer language structure and esoterica). I do remember, however, building a simple program that would translate any declaration into English.
The golden rule
The rule goes like this:
The degenerate case is:
How To Read C Declarations
zz from http://blog.chinaunix.net/u1/36290/showart_443799.html
Even experienced C programmers have difficulty reading declarations that go beyond simple arrays and pointers. For example, is the following an array of pointers or a pointer to an array?
int *a[10];
What the heck does the following mean?
int (*(*vtable)[])();
Naturally, it's a pointer to an array of pointers to functions returning integers. ;)
This short article tells you how to read any C declaration correctly using a very simple technique. I am 99% certain that I read this in a book in the late 1980s, but I can't remember where. I doubt that I discovered this on my own (even though I've always been delighted by computer language structure and esoterica). I do remember, however, building a simple program that would translate any declaration into English.
The golden rule
The rule goes like this:
引用
"Start at the variable name (or innermost construct if no identifier is present. Look right without jumping over a right parenthesis; say what you see. Look left again without jumping over a parenthesis; say what you see. Jump out a level of parentheses if any. Look right; say what you see. Look left; say what you see. Continue in this manner until you say the variable type or return type."
The degenerate case is:
Feb
19
众所皆知,以下代码可以实现两个变量的交换:
于是用这个写了一个随机化快速排序(防止特殊情况下的算法退化,代码很简单,如果认识快排,应该不难读懂)
看起来不错,可是运行结果不对,为什么呢?
因为
int a, b;
a = a ^ b;
b = a ^ b;
a = a ^ b;
a = a ^ b;
b = a ^ b;
a = a ^ b;
于是用这个写了一个随机化快速排序(防止特殊情况下的算法退化,代码很简单,如果认识快排,应该不难读懂)
void qsort(int a[], int s, int e){
int i = s, j = e, t, p;
if (s < e){
p = rand() % (e - s + 1) + s;
a[s] ^= a[p], a[p] ^= a[s], a[s] ^= a[p];
t = a[s];
while(i != j){
while(i < j && a[j] > t) j--;
if(i < j) a[i++] = a[j];
while(i < j && a[i] < t) i++;
if(i < j) a[j--] = a[i];
}
a[i] = t;
qsort(a, s, i - 1);
qsort(a, i + 1, e);
}
}
int i = s, j = e, t, p;
if (s < e){
p = rand() % (e - s + 1) + s;
a[s] ^= a[p], a[p] ^= a[s], a[s] ^= a[p];
t = a[s];
while(i != j){
while(i < j && a[j] > t) j--;
if(i < j) a[i++] = a[j];
while(i < j && a[i] < t) i++;
if(i < j) a[j--] = a[i];
}
a[i] = t;
qsort(a, s, i - 1);
qsort(a, i + 1, e);
}
}
看起来不错,可是运行结果不对,为什么呢?
因为
Feb
11
今天回顾汇编,顺手写了一个,居然还可以运行,挖咔咔,自恋一下
datas segment use16
str1 db "hello world!", 0dh, 0ah, "$"
datas ends
stacks segment use16
db 256 dup(0)
stacks ends
codes segment use16
assume cs:codes, ds:datas, ss:stacks
start: mov ax, datas
mov ds, ax
lea dx, str1
mov ah, 09h
int 21h
mov ah, 4ch
int 21h
codes ends
end start
Feb
5
这篇文章尾烂了,大家多给点意见,好充实其中的内容;也提出里面的不足。
---
我觉得这样的文章应该有人写过的,但是Google里面貌似没有(或许有英文版)
Baidu给了一个,不过不是很像样 http://baike.baidu.com/view/94274.htm
那我就写一个吧,这也是momodi大牛在上个学期初委托给我的一件事情。
这篇文章面向的对象是没有多少基础,或者是才学C语言或数据结构的同鞋们。
--
首先,什么是acm/icpc?ACM/ICPC(ACM International Collegiate Programming Contest, 国际大学生程序设计竞赛)是由国际计算机界历史悠久、颇具权威性的组织ACM(Association for Computing Machinery,国际计算机协会)主办的,世界上公认的规模最大、水平最高的国际大学生程序设计竞赛。
——这段定义来自 百度百科 -> ACM/ICPC,其实说简单了就几个字:想算法,写程序,解题目。
---
我觉得这样的文章应该有人写过的,但是Google里面貌似没有(或许有英文版)
Baidu给了一个,不过不是很像样 http://baike.baidu.com/view/94274.htm
那我就写一个吧,这也是momodi大牛在上个学期初委托给我的一件事情。
这篇文章面向的对象是没有多少基础,或者是才学C语言或数据结构的同鞋们。
--
首先,什么是acm/icpc?ACM/ICPC(ACM International Collegiate Programming Contest, 国际大学生程序设计竞赛)是由国际计算机界历史悠久、颇具权威性的组织ACM(Association for Computing Machinery,国际计算机协会)主办的,世界上公认的规模最大、水平最高的国际大学生程序设计竞赛。
——这段定义来自 百度百科 -> ACM/ICPC,其实说简单了就几个字:想算法,写程序,解题目。
Jan
11
Jan
9
这是今天算法考试的最后一题,设计一个动态规划的算法来求两个字符串的编辑距离。
以为自己做对了,很happy —— 其实错了,好笨,sigh.
--
Levenshtein Distance (LD, 来文史特距离)也叫edit distance(编辑距离),它用来表示2个字符串的相似度,LD定义为需要最少多少步基本操作才能让2个字符串相等,基本操作包含3个:插入, 删除, 替换;比如,kiteen和sitting之间的距离可以这么计算:
1,kitten -- > sitten, 替换k为s;
2,sitten -- > sittin, 替换e为i;
3,sittin -- > sitting, 增加g;
所以,其LD为3。
设计状态d[m][n] = d(A[1..m], B[1..n]),易知:
d[0][0] = 0;
d[i][0] = i;
d[0][j] = j;
d[i][j] = min( d[i-1][j-1] + (If A[i]=B[j] Then 0 Else 1 End If), //修改一个字符
d[i-1][j] + 1, //插入一个字符
d[i][j-1] + 1 //删除一个字符
于是可以递推地填满一个 m * n 的矩阵,即得答案。
计算LD的算法表示为(C++代码):
这个算法其实就是一个矩阵的计算:
最后的d[m][n]就是求得的答案。
@ 2009-06-14
贴一个优化空间复杂度为O(n)的代码(滚动数组):
以为自己做对了,很happy —— 其实错了,好笨,sigh.
--
Levenshtein Distance (LD, 来文史特距离)也叫edit distance(编辑距离),它用来表示2个字符串的相似度,LD定义为需要最少多少步基本操作才能让2个字符串相等,基本操作包含3个:插入, 删除, 替换;比如,kiteen和sitting之间的距离可以这么计算:
1,kitten -- > sitten, 替换k为s;
2,sitten -- > sittin, 替换e为i;
3,sittin -- > sitting, 增加g;
所以,其LD为3。
设计状态d[m][n] = d(A[1..m], B[1..n]),易知:
d[0][0] = 0;
d[i][0] = i;
d[0][j] = j;
d[i][j] = min( d[i-1][j-1] + (If A[i]=B[j] Then 0 Else 1 End If), //修改一个字符
d[i-1][j] + 1, //插入一个字符
d[i][j-1] + 1 //删除一个字符
于是可以递推地填满一个 m * n 的矩阵,即得答案。
计算LD的算法表示为(C++代码):
int d[1010][1010];
int dist(string a, string b){
int m = a.size(), n = b.size(), i, j;
for(i = 0; i <= m; ++i) d[i][0] = i;
for(j = 0; j <= n; ++j) d[0][j] = j;
for (i = 1; i <= m; ++i){
for(j = 1; j <= n; ++j){
// -------------- a, b是从0开始计数的
d[i][j] = d[i-1][j-1] + (a[i-1]==b[j-1]?0:1); //修改一个字符
d[i][j] = min(d[i][j], d[i-1][j] + 1); //插入一个字符
d[i][j] = min(d[i][j], d[i][j-1] + 1); //删除一个字符
}
}
for (i = 0; i <= m; ++i){ //打印矩阵
for(j = 0; j <= n; ++j)
printf("%5d ", d[i][j]);
printf("\n");
}
return d[m][n];
}
int dist(string a, string b){
int m = a.size(), n = b.size(), i, j;
for(i = 0; i <= m; ++i) d[i][0] = i;
for(j = 0; j <= n; ++j) d[0][j] = j;
for (i = 1; i <= m; ++i){
for(j = 1; j <= n; ++j){
// -------------- a, b是从0开始计数的
d[i][j] = d[i-1][j-1] + (a[i-1]==b[j-1]?0:1); //修改一个字符
d[i][j] = min(d[i][j], d[i-1][j] + 1); //插入一个字符
d[i][j] = min(d[i][j], d[i][j-1] + 1); //删除一个字符
}
}
for (i = 0; i <= m; ++i){ //打印矩阵
for(j = 0; j <= n; ++j)
printf("%5d ", d[i][j]);
printf("\n");
}
return d[m][n];
}
这个算法其实就是一个矩阵的计算:
引用
调用dist("abcdef", "acddaf")可以得到输出为:
0 1 2 3 4 5 6
1 0 1 2 3 4 5
2 1 1 2 3 4 5
3 2 1 2 3 4 5
4 3 2 1 2 3 4
5 4 3 2 2 3 4
6 5 4 3 3 3 3
0 1 2 3 4 5 6
1 0 1 2 3 4 5
2 1 1 2 3 4 5
3 2 1 2 3 4 5
4 3 2 1 2 3 4
5 4 3 2 2 3 4
6 5 4 3 3 3 3
最后的d[m][n]就是求得的答案。
@ 2009-06-14
贴一个优化空间复杂度为O(n)的代码(滚动数组):
int diff(char *a, char *b){
int *d[2], i, j;
int m = strlen(a), n = strlen(b);
d[0] = new int[n + 1];
d[1] = new int[n + 1];
int turn = 0, pre, t;
for (i = 0; i <= n; ++i) d[turn][i] = i;
for (i = 1; i <= m; ++i){
pre = turn;
turn = (turn + 1) % 2;
d[turn][0] = i;
for(int p=0;p<=n;p++)printf("%d ",d[pre][p]);printf("\n");
for(j = 1; j <= n; ++j){
t = d[pre][j-1] + (a[i-1] == b[j-1] ? 0 : 1);
t = min(t, d[pre][j] + 1);
d[turn][j] = min(t, d[turn][j-1] + 1);
}
}
for(int p=0;p<=n;p++)printf("%d ",d[turn][p]);printf("\n");
t = d[turn][n];
delete[] d[0];
delete[] d[1];
return t;
}
int *d[2], i, j;
int m = strlen(a), n = strlen(b);
d[0] = new int[n + 1];
d[1] = new int[n + 1];
int turn = 0, pre, t;
for (i = 0; i <= n; ++i) d[turn][i] = i;
for (i = 1; i <= m; ++i){
pre = turn;
turn = (turn + 1) % 2;
d[turn][0] = i;
for(int p=0;p<=n;p++)printf("%d ",d[pre][p]);printf("\n");
for(j = 1; j <= n; ++j){
t = d[pre][j-1] + (a[i-1] == b[j-1] ? 0 : 1);
t = min(t, d[pre][j] + 1);
d[turn][j] = min(t, d[turn][j-1] + 1);
}
}
for(int p=0;p<=n;p++)printf("%d ",d[turn][p]);printf("\n");
t = d[turn][n];
delete[] d[0];
delete[] d[1];
return t;
}
Jan
8
其实好简单的=.= 为啥以前没去看捏。。。
已知有n个东东为u[1]~u[n], 体积和重量分别是s[1]~s[n]和w[1]~w[n]
有一个包包可以装下大小为S的东东,求这个包包最重可以装多重。
记W[n][S]为要求的东西: 从n个东东里面取出一部分填满体积为S的包包
很容易理解,W[n][S]为以下两个取值的最大值:
1. W[n-1][S] 不取第n个东东,最多可以装多重
2. (s[n] <= S的时候) w[n] + W[n-1][S-s[n]] 一定取第n个东东,重量为v[n],包包还能装S-s[n],那么前n-1个东东最多可以装多重。
另外,显然可以看出,当n或者S为0的时候,W[n][S]肯定也为0。
最简单的是写一个递归:
已知有n个东东为u[1]~u[n], 体积和重量分别是s[1]~s[n]和w[1]~w[n]
有一个包包可以装下大小为S的东东,求这个包包最重可以装多重。
记W[n][S]为要求的东西: 从n个东东里面取出一部分填满体积为S的包包
很容易理解,W[n][S]为以下两个取值的最大值:
1. W[n-1][S] 不取第n个东东,最多可以装多重
2. (s[n] <= S的时候) w[n] + W[n-1][S-s[n]] 一定取第n个东东,重量为v[n],包包还能装S-s[n],那么前n-1个东东最多可以装多重。
另外,显然可以看出,当n或者S为0的时候,W[n][S]肯定也为0。
最简单的是写一个递归: