显示标签为“ACM”的博文。显示所有博文
显示标签为“ACM”的博文。显示所有博文

2012年12月4日星期二

SCUTOJ 建站手记(3)--- 压力测试 & 性能监控

又是抽一些空隙时间,基本完成了SCUTOJ的压力测试,参考的是《构建高性能web站点》,但是过程中还是遇到不少问题,这里记录下。

硬件环境:

首先是在本机用 webbench 这个小巧的测试工具对远端的服务器进行测试。小是小了,可惜功能太少,结果太简单。


可以看到,并发数分别为:100, 500,1000,2000,持续时间均为30秒。
页面响应速度分别为: (pages/min)
5244
5220
6950   fail : 0.7 %
8022   fail : 2%

从1000开始已经出现fail了, 并且随着并发数增加,失败概率不断增大。

再看一下apache mod_status 的数据:

空闲的时候其实只有4个线程(worker),并发数500的时候已经升到138了,到1000的时候好像也没有大的变化。看了一下apahce2.conf,maxClient设置为150.

由于webbench测试是在图书馆做的,带宽还是挺不错的,看了一下下行速度,有1M多每秒。应该说线路带宽当面对结果不会有太大影响,延迟就不敢保证了 = =

2012年11月23日星期五

SCUTOJ 建站手记(2)

由于这一段时间一直在忙各种实验和课设,而且目测还要忙到差不多期末,然后又差不多要考试,所以总体来说项目进度比较慢,还是断断续续的。目前只能说把网站搭起来了,但是还需要继续深入进行二次开发,而前提是需要先大概过一遍源代码,某些关键的地方还要多加留意才是。
目前主要的问题是没有专业的web前端人员啊!只能是在下先大体改了一下界面,但是毕竟不是专业美工,效果肯定不咋的。。。今天刚好有时间,就收拾了一下界面,顺便把域名弄好了(上次注册到的tk域名竟然被回收了,完全不明所以啊 = =希望这次没事。。。)

=========================
由于在理想规划中是不止一个子域名的,这就需要用到基于域名的virtualhost配置了。关于apache虚拟主机的配置网上有很多,这里就简单说一下。

#环境:ubuntu 10.04 LTS 
#创建virtualhost配置文件
cd /etc/apache2/sites-available/
copy default scutoj      //copy一份默认的配置再修改,比较方便

其实要改的只有几处:

<VirtualHost *:80>        //服务器只有单IP的情况下这里不需要修改(个人理解)
ServerName www.example.com              //自定域名
DocumentRoot /var/www/                //这个有需要的话可以该,我将它指向OJ的根目录了,也就是直接访问IP的话就进入OJ的根目录index页面,以前要手工输入目录名,不然就是默认apache的index.html
其他貌似没什么改动了,还有一个ErrorDocument的参数可以添加,可以自定义出错页面,比如diy的404...


然后就是启用域名:
sudo a2ensite scutoj    //apache2 enable site的缩写,其实就是在sites-enable/里创建symbol link,指向VH的配置文件
sudo service apache2 restart

关闭网站服务: sudo a2dissite scutoj
====================

接下来的计划其实蛮激动人心的 = =!!
因为自从我在SJUT-NIC回来之后,就对web application产生了相当大的兴趣,对web架构,web服务器,数据库以及众多的web开源框架与应用之类的都有莫名的冲动~希望能自己亲手实现一下。
在目前OJ的系统当中,只提供了基本的judging和contest等等服务,原生论坛太搓,所以准备自己搭一个论坛挂在主站上面,想试试Rable(基于Livid的PB2)。最近泡V2EX比较多,觉得论坛的话还是这种比较好~更重要是的想玩玩Ruby on Rails啊!(其实《python学习手册》我都借回来了,暂时还没时间看...但是ruby对我来说更有神秘感,嘛,可以都看看,到时再决定用python或者ruby或者PHP。
另外还需要一个关于ACM的文档和心得体会分享平台,用mediawiki搭一个wiki感觉上是个不错的方式。个人也很喜欢wiki的分享方式,只是不知道实际运营起来效果怎么样。

这样算下来,二级域名下面就有三个子站了,还是蛮不错的~有得折腾~

其实这些天一直在想另外一个很现实的问题,就是:OJ该由谁来负责长期运营和维护呢?在下是真心不想交给学院,事实证明无论什么东西,到了行政机关手里都会变成一堆渣渣 = =
在下希望这是一个有活力的project,以至于product。所以一个好的管理员或者管理团队是必须的,就好象Livid一个人给整个V2EX注入了灵魂,这个社区才能通过口碑吸引不少高质量的用户。这方面感觉其实SYSU做的挺不错的,他们的官方OJ是由ACMM协会负责维护的貌似,还有一个很萌的吉祥物sicily。POJ或者HDOJ都比较正式和官方的感觉,但毕竟是老牌,所以题库和用户积累是个很重要的因素。

综上所述,SCUTOJ要走的路还很远啊。。。希望不要半途夭折就好Orz

PS:近期准备学markdown,在github page上个octopress,因为blogspot实在太蛋疼了。。。希望在下次有需要写post之前能弄好吧。。。

2012年11月4日星期日

SCUTOJ 建站手记

鉴于某学院的说法:你们不做出点样子(UI)来,我怎么好给你服务器呢??。。。 = =
好吧,既然这么蛋疼就先在VPS上面搞好了。。。(一上来就吐槽学院好像不太好的样子??

主机的话,原来想选Linode的,心想咱们也好不容易可以做一回壕啊。最后考虑到毕竟是测试阶段,服务器没必要选的那么好,所以退而求其次,选了xehost(在V2EX看到的,刚好碰上搞活动的说)。XEN主机,美国机房,ChinaCache线路,电信ping值在150-200ms之间,勉强还可以接受。价格的话略贵吧,月付80大洋。

硬件配置:
Xeon E5606 @2.13GHz 双核
512M RAM
20G 空间
500G 流量
100Mbps 带宽 (实测有接近2M/s的速度
磁盘I/O 测试了一下,40M/s, 勉强吧
系统 Ubuntu 10.04

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

hustoj代码托管在google code,要用SVN checkout(其实我还是更喜欢github)

svn checkout http://hustoj.googlecode.com/svn/trunk/ hustoj-read-only

进入到hustoj-read-only/install 目录中,修改一下安装脚本,把yum系的去掉,注意下数据库的用户和密码就可以运行了。其实最好就是手动安装一次,这样对整个架构就有个大体的把握了。

还有web服务器关于php脚本的设置:
       sudo vim /etc/php5/apache2/php.ini 
       open_basedir =/home/judge/data:/var/www/JudgeOnline:/tmp  
       max_execution_time = 300     ; Maximum execution time of each script, in seconds
       max_input_time = 600 
       memory_limit = 256M      ; Maximum amount of memory a script may consume (16MB)
       post_max_size = 64M
       upload_tmp_dir =/tmp
       upload_max_filesize = 64M
设置完以后重启apache。

在浏览器输入IP/目录,应该可以看到页面了。如果出现网页一片空白的情况,说明php脚本有语法错误或者web文件目录权限设置不对。这时候可以打开php.ini的display_error 选项,可以方便排除错误。debug完之后建议还是关上。默认,php报错是不记录进日志文件,这很不便于排查问题。打开php的错误日志记录也很简单。编辑php.ini
log_errors = On
error_log = /usr/local/php/log/error.log

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

接下来就是折腾DNS了。
偶在dot.tk上面注册了scutoj.tk的域名,其实也只是实验阶段,免费东西不知道靠谱不,到时有条件还是转.me好了(其实很想注册个像中大的那种短域名啊soj.me )。其实原来想用DNS Pod的服务(据说对google DNS和open DNS有优化,不过按照最近斯巴达的网络状况,8.8.8.8都全挂了 = =),但是懒了一下,暂时没弄。

后台添加一个A record,指向VPS的IP地址,主机名就写www。但是这样的话只是指向/var/www目录,而OJ是放在其子目录下。以前没试过子目录绑定域名,又是google上一阵狂搜,折腾啊~

最后发现是需要设置apache。这里又涉及到各种linux distribution的apahce配置文件位置不同名字不同 = =!,最后还是在ubuntu wiki上面找到官方说明,是设置apahce virtualhost。具体可以看这个链接。还有就是NameVirtualHost *:80 ,这里在 /etc/apache2/port.conf 里面已经设置过了,再在default里面设的话可能会报warn。只需要注释掉其中一个即可。

我记得以前在SJTU-NIC的时候有看过下apache的配置文件,过了几个月竟然忘得七七八八了。。。哎,这记忆力,跪了。




2012年11月3日星期六

基于OpenStack的Online Judge实现构想

应该说吧,这个构想在po主接下老师项目的时候就有点思路了。当时刚好接触openstack不久,对云计算有点概念。感觉openstack和OJ是可以结合起来的,当然具体的想法是经过了解openstack以后渐渐的明朗起来。

先交代下目前的状况。某理工作为国内排得上号的211,985传统理工学校,还没有一个像样的OJ系统。所谓的不像样,就是该OJ即使是面对两个班(大概一百多号人)同时上机实验都会随时崩溃...对于校赛这种就更不用说了,我们学校的ACM集训队从来不敢用我们自己的OJ,都是到杭电上面去搞contest...

Online Judge某种程度上也算是SaaS,为用户提供在线判题服务。用户把代码提交上去,服务器经过计算将结果返还给用户,典型的B/S架构。这样说的话,其实是单节点服务器就足以应付的小型应用(对于我们大SCUT来说还真是= =!)。但是单纯从web架构方面考虑,这里面还有很多需要改进的地方。最简单直接而且立竿见影的方案是,将LAMP架构换成LNMP架构,也就是apache + mod_php 改用 nginx + fastCGI 以提高系统并发量,加快PHP解释速度。。。至于动态网页静态化,数据缓存之类的优化方案也是可以有的,而且肯定会实现。

po主的想法是,如果由openstack在其中作为IaaS层向上提供服务,可以在物理服务器有限的前提下方便的搭建分布式判题系统,对外由nginx作为web前端节点,并为后面的判题节点集群作负载均衡。由于云计算“按需分配”的特性,可以较好的实现系统资源弹性分配,避免单服务器节点在高负载下成为系统瓶颈,同时也增强了可扩展性。虽然我们的初始需求仅仅是满足教学日常用途,但是在一开始就设立架构上的大方向,考虑到以后的可扩展性的话,目光放远点还是很有必要的。

同时po主准备基于hustOJ的系统进行二次开发,添加一些个性化的模块,比如mediawiki用作管理OJ的技术记录与使用说明。看了该项目的源代码一段时间,觉得原作者真是牛逼闪闪啊。框架暂时没发现什么问题,后期管理员的优化也很不错,这不得不说是开源的一大好处。貌似我越来越喜欢open source了~(才不是什么拿来主义呢

2012年4月2日星期一

merge sort & inversion

inversion 逆序数
definition: 
对数组a[].存在一对(i,j)有i < j 且 a[i] > a[j] 即为一个逆序数对
e.g.
{1,3,5,2,4,6} 逆序数为3
 i.e. (3,2) (5,2) (5,4)
Stanford open course 的第一章就是关于分而治之思想(divide and conquer)的归并排序(mergesort)。上学期去隔壁班蹭算法课的时候听了下内排序,其实理解的不够深刻。现在再看一次mergesort,确实将分治思想变现的淋漓尽致。将大问题划分成小问题,递归调用函数去解决。
mergesort的大致思路,就是将一个数组平均分成左右两个子数组,分别调用排序函数本身,返回的是两个已排序的数组,然后再将其merge起来。时间复杂度是O(n·log n)

在mergesort算法的基础上稍为改变一下就可以实现求一组数的逆序数。
单纯用for循环模拟的话,复杂度是O( n^2 ) 即n个数中取2个的组合数。
接上,一个array被分成left array[i] & right array[j],其逆序数的组成可分为3部分:
1.left inversion     (i,j)均在left array中,即 i,j <= n/2   
2.right inversion     (i,j)均在right array中,即 i,j > n/2
3.split inversion      i在left,j在right,即 i <= n/2 ; j > n/2

计算左数组和右数组的逆序数,再加上split inversion即可得到整个数组的inversion number。问题关键成了解决split inversion。假设左右数组均已排好序(sorted B[],C[]),按mergesort的思路将两个数组合并的时候,每次从B[]取数放到合并数组(D[]),
计算B中剩余的元素个数(因为B中元素本应全部小于C中元素,若不是则必然存在逆序)。

课程练习原题是从100000的样本测试数据(txt)中计算逆序数个数,这个mergesort的模板感觉是有点怪(参数列表的问题)。开始用int count,结果溢出了。。。目测一下结果,貌似溢出的不多,改unsigned int,过了~

上代码:
#include<cstdio>
#include<fstream>
using namespace std;
int tmp[100001],a[100001];
void mergesort(int a[],int tmp[],int left,int right,unsigned int& count){   //待排序数组地址a,缓存数组tmp(节省时间,避免每次递归调用都开辟新数组)
int mid = (left + right) / 2;
if(left == right)return;     //递归分割的最小子项
mergesort(a,tmp,left,mid,count);      
mergesort(a,tmp,mid + 1,right,count);   //分别对左右数组递归调用mergesort(invoke mergesort() recursively)
for(int i = left;i <= right;i ++)tmp[i] = a[i];
int i = left;
int j = mid + 1;
for(int k = left;k <= right;k ++){    //merge two sorted array
if(i > mid)a[k] = tmp[j ++];      //judge the index of array out of size 
else if(j > right)a[k] = tmp[i ++];
else if(tmp[i] > tmp[j]){
a[k] = tmp[j ++];
count += mid + 1 - i;         //add remain number of left array(inversions with tmp[j])
}
else a[k] = tmp[i ++];
}
}
int main()
{
fstream file("d:\\IntegerArray.txt",ios::in);
if(!file)printf("exception!");
int i = 0;
unsigned int count = 0;
char s[10];
while(file >> s)
a[i ++] = atoi(s);
mergesort(a,tmp,0,99999,count);
printf("%u\n",count);
return 0;
}


2012年2月19日星期日

HDOJ 2035 人见人爱A ^ B

突然想起还有一份杭电ACM的PPT没看,翻出来看了下~发觉蛮有意思的,就是内容少了点。。。基本上是提纲,该总结的都自己来吧。╮( ̄▽ ̄")╭

题目很简单,但也蛮经典的算术题。
输入两个数A,B。输出A ^ B的最右三位数。

核心思路:求 A的B次方 模1000,只需要求 A%1000 的 B次方。B通过降幂二分加速。
比如:(1123)^ 6 = (1000 + 123) ^ 6
多项式展开后,决定结果最后三位的是123 ^ 6
然后123 ^ 6 = (123 ^ 3) ^ 2

version 1.0  纯暴力

#include<cstdio>
int main()
{
int a,b,sum;
while(scanf("%d%d",&a,&b) && a){
sum = a;
for(int i = 1;i < b;i ++){
sum *= a;
if(sum >= 1000)sum %= 1000;
}
printf("%d\n",sum);
}
return 0;
}



优化之后,通过二分加速,int范围内的大数都可以接受。
version 1.1 递归

2012年2月10日星期五

POJ 2247 humble number (DP)


这题第一感觉就是筛数法,可能是受前几天刚做的那题影响了。。。其实思路也没有太大问题,小数据的话还是可以的(虽然没优化过 = =)结果没想到题目的数据太大,堆溢出了 = =!所以就只好换一种思路吧。。。。
这题和ugly number 那题想法是可以完全一样的,还算蛮清晰,就是输出那里比较变态,估计坑了不少人。。。
网上看到有链表的做法,感觉上比这个要繁琐了,其实就是经典动规题。
核心思路就是用2,3,5,7和humble list中的数相乘,得出的数依然在list之内。
假设humble number集合开始只有{1}
2,3,5,7分别从集合中由小到大取数相乘后,
即2 * 1,3 * 1,5 * 1,7 * 1,min = 2,入队列{1,2}。
然后比较2 * 2,3 * 1,5 * 1,7 * 1,min = 3,入队列{1,2,3}。
.....


#include<cstdio>
int humble[6000];
int main(){
int t2,t3,t5,t7,p2,p3,p5,p7,i,min;
p2 = p3 = p5 = p7 = 1;
humble[1] = 1;
for(i = 1;i < 5843;i ++){
t2 = humble[p2] * 2;
t3 = humble[p3] * 3;
t5 = humble[p5] * 5;
t7 = humble[p7] * 7;
min = t2 < t3 ? t2 : t3;
min = min < t5 ? min : t5;
min = min < t7 ? min : t7;
humble[i+1] = min;
if(min == t2)p2 ++;
if(min == t3)p3 ++;
if(min == t5)p5 ++;
if(min == t7)p7 ++;
}
while(scanf("%d",&i) && i){
if(i % 10 == 1){
if(i % 100 == 11)printf("The %dth humble number is %d.\n",i,humble[i]);
else printf("The %dst humble number is %d.\n",i,humble[i]);
continue;
}
if(i % 10 == 2){
if(i % 100 == 12)printf("The %dth humble number is %d.\n",i,humble[i]);
else printf("The %dnd humble number is %d.\n",i,humble[i]);
continue;
}
if(i % 10 == 3){
if(i % 100 == 13)printf("The %dth humble number is %d.\n",i,humble[i]);
else printf("The %drd humble number is %d.\n",i,humble[i]);
continue;
}
else printf("The %dth humble number is %d.\n",i,humble[i]);
}
return 0;
}



2012年2月4日星期六

POJ 2739 Sum of Consecutive Prime Numbers

打表,大水,RE 3次WA 1次。。。我原来堕落到这么水的程度了。。。囧~
虽然说在discuss里看到有10001的数据,但终究是数组开小了,而且循环条件没处理好。
开始想这么打表会不会太耗时效率太低?。。。结果0ms瞎了我的眼 = =
想个筛数法还回忆了好一阵,我勒个去!


#include<cstdio>
const int MAX = 10050;
int number[10050] = {1,1,0};
int prime_list[2000];
int ans[MAX];
int main()
{
int i,j,k,sum = 0;
for(i = 2;i < 105;i ++)
if(number[i] == 0)
for(j = i * i;j < 10030;j += i)number[j] = 1;
for(i = 2,k = 0;i < 10030;i ++)
if(number[i] == 0){prime_list[k] = i;k ++;}      //prime number list
for(i = 0;prime_list[i] < 10030 && prime_list[i] > 0;i ++){     //here forget the rest 0 are also below 10030 = =
for(j = i;sum < 10030;j ++){
ans[sum] ++;
sum += prime_list[j];
}
sum = 0;
}
while(scanf("%d",&i) && i)
printf("%d\n",ans[i]);
return 0;
}

2012年1月24日星期二

POJ 1750 Dictionary 模拟水题

这题目最后说的blank line number着实让人摸不着头脑,也就不管了。。。算法也没什么太多可以说的,就是做完之后发现这runtime 700+ms... = =!
网上有影射的code,发觉效率真的高不少啊,其实就是将for输出的blank用影射实现了,确实节省一定的时间。。。细节问题呢~总是想不到这种取巧的方法,是coding太少,还没有这意识吧...囧



#include<cstdio>
const int N = 100001,M = 11;
char list[N][M];


int main()
{
int i = 0,j = 0,total = 0;
while(scanf("%s",list[total]) != EOF)
total ++;
int blank = 0,substr = 0;
printf("%s\n",list[0]);
for(i = 1;i < total;i ++){
j = substr = 0;
while(list[i][j] != '\0'){      //control the space number whitout "0",TLE for this T_T
if(list[i][j] != list [i - 1][j])break;
else substr ++;
j ++;
}
if(substr > blank)blank ++;      //main algorithm
else blank = substr;
for(int k = 0;k < blank;k ++)
printf(" ");
printf("%s\n",list[i]);
}
return 0;
}

2012年1月15日星期日

POJ 2159 Ancient Cipher(hash)

开始也是被简单的题目坑了。。。。考完试之后的第一道练手题啊,悲催 T_T
再细心看了一次之后,思路还是蛮清晰的。就是要求字符串中不同字母出现的次数一一对应。比如ABABC和DDEEF就应该是yes,如果是DDEEE就是no。但实现起来还是嫌麻烦。总是觉得有太多不需要的语句,很臃肿。涉及快排啊,比较啊。然后看到一个hash的算法就果断跪了 = =

题目归结为判断两个自然数多集(允许有重复元素的集合)a, b是否相等。用快排比较的效率是O(nlgn),下面提出一种O(n)的方法供大家参考。
 猜想:若 sum(a^i) = sum(b^i),i = 0, 1, 2, 则 a = b。 
证明:略。。。俺数学也不好  = = 


注意 :sum(s^0) 是多集s中元素的数目 
      sum(s^1) 是多集s中元素的和 
      sum(s^2) 是多集s中元素的平方和




追求代码的简洁之道。与其随便的实现,不如多花些时间去想。。。从小就懒啊,没办法



2011年12月10日星期六

POJ 1753 Flip Game 暴力。。。囧


这题跟先前那题基本一样,就不多说了 = =
很郁闷的是一样的题目一样的思路竟然效率还是那么低,总会有这样那样的小问题,然后debug来debug去,我勒个去啊~~~~~~
上网找了一下,用BFS + 位操作。。。。
喵的好高级 = =
队列实现的BFS。。。寒假回去恶补搜索啊囧~~


#include”cstdio”
#include”cstring“
#include”cstdlib“
char table[6][7];
char copy[6][7];
int firstline[5],count;
void change(int a,int b)
{
if(copy[a][b] == 'b')copy[a][b] = 'w';else copy[a][b] = 'b';
if(copy[a-1][b] == 'b')copy[a-1][b] = 'w';else copy[a-1][b] = 'b';
if(copy[a][b-1] == 'b')copy[a][b-1] = 'w';else copy[a][b-1] = 'b';
if(copy[a+1][b] == 'b')copy[a+1][b] = 'w';else copy[a+1][b] = 'b';
if(copy[a][b+1] == 'b')copy[a][b+1] = 'w';else copy[a][b+1] = 'b';
count ++;
}


int main()
{
int i,j,t,min = 16;
for(i = 1;i < 5;i ++)
scanf("%s",&table[i][1]);
for(t = 0;t < 16;t ++){
memset(firstline,0,sizeof(firstline));
int tmp = t,k = 1;          //WTF = =here the problem k which got me in trouble
while(tmp){                       //create a binary array
firstline[k] = tmp % 2;
tmp /= 2;
k ++;
}


memcpy(copy,table,sizeof(table));      //try to change to all white
count = 0;
for(k = 1;k < 5;k ++)
if(firstline[k] == 1)change(1,k);
for(i = 2;i < 5;i ++)
for(j = 1;j < 5;j ++){
if(copy[i-1][j] == 'b')change(i,j);
/*test function
for(int m = 1;m < 5;m ++){
for(int n = 1;n < 5;n ++)
printf("%c ",copy[m][n]);
printf("\n");
}
system("pause");
system("cls");
*/
}
for(j = 1;j < 5;j ++)
if(copy[4][j] != 'w')break;
if(j == 5 && count < min)min = count;   //mark the min


memcpy(copy,table,sizeof(table));     //try to change to all black
count = 0;
for(k = 1;k < 5;k ++)
if(firstline[k] == 1)change(1,k);
for(i = 2;i < 5;i ++)
for(j = 1;j < 5;j ++)
if(copy[i-1][j] == 'w')change(i,j);    //difference!
for(j = 1;j < 5;j ++)
if(copy[4][j] != 'b')break;
if(j == 5 && count < min)min = count;
}
if(min != 16)printf("%d\n",min);
else printf("Impossible\n");
return 0;
}

2011年12月8日星期四

POJ 2328 Guessing Game 超水 = =


这么水的题,怎么坑了那么多人呢? = =
想不懂
还有32ms的C++过的。。。囧
真不明白这什么过程弄的。。。。




#include”cstdio“
int main()
{
int max = 11,min = 0,t;
char s1[10],s2[10];
while(scanf("%d",&t) && t){
scanf("%s%s",s1,s2);       //原先用gets出问题(应该是get了\n),干脆就用这个了= =
if(s2[0] == 'h' && t < max)max = t;
if(s2[0] == 'l' && t > min)min = t;
if(s2[0] == 'o'){
if(t < max && t > min)printf("Stan may be honest\n");
else printf("Stan is dishonest\n");
max = 11;
min = 0;
}
}
return 0;
}

2011年12月3日星期六

百练 2754 八皇后 DFS经典题,递归,枚举

开始研究DFS好久也只弄懂个大概。。。。回溯是个问题。。。。
模拟起来总是有一定难度 = =!!
后来想一下,好像弄个全排列时间复杂度也不是很大啊。。。
试试枚举 = =
手写半小时,0ms AC....
囧~


代码水的可以。。。 = =
直接打表水过去了。。。。


#include”cstdio“
#include”algorithm“
#include”cmath“
#include“memory.h”
using namespace std;
int queen[8]={1,2,3,4,5,6,7,8};
int res[92][8];
int main()
{
int i = 0,j,k;
while(next_permutation(queen,queen + 8)){
for(j = 0;j < 7;j ++){
for(k = j + 1;k < 8;k ++)
if(abs(queen[j] - queen[k]) == abs(j - k))break;    //在对角线上
if(k != 8)break;
}
if(j == 7 && i < 92){
memcpy(res[i],queen,sizeof(queen));
i ++;
}
}
scanf("%d",&i);
while(i --){
scanf("%d",&j);
for(k = 0;k < 8;k ++)
printf("%d",res[j - 1][k]);
printf("\n");
}
return 0;
}




随后贴上DFS代码。。。。
导引上的解法,加了调试程序细心看了一次
感觉上效率不算太高。虽然模拟的过程是不错的,思想是值得学习的

#include“stdio.h”
#include“math.h”
#include“cstdlib”
int queenPlaces[92][8]; //存放92 种皇后棋子的摆放方法
int count = 0;
int board[8][8]; //仿真棋盘
void putQueen(int ithQueen); //递归函数,每次摆好一个棋子
int main()
{
int n, i, j;
for(i = 0; i < 8; i++){ // 初始化
for(j = 0; j < 8; j++)
board[i][j] = -1;
for(j = 0; j < 92; j++)
queenPlaces[j][i] = 0;
}
putQueen(0); //从第0 个棋子开始摆放,运行的结果是将queenPlaces 生成好
scanf("%d", &n);
for(i = 0; i < n; i++){
int ith;
scanf("%d", &ith);
for(j = 0; j < 8; j++)
printf("%d", queenPlaces[ith - 1][j]);
printf("\n");
}
return 0;
}
void putQueen(int ithQueen)
{
int i, k, r;
if(ithQueen == 8){
count ++;
return;
}
for(i = 0; i < 8; i++){
if(board[i][ithQueen] == -1){
//摆放皇后
board[i][ithQueen] = ithQueen;
//将其后所有的摆放方法的第ith 个皇后都放在i+1 的位置上
//在i 增加以后,后面的第ith 个皇后摆放方法后覆盖此时的设置
for(k = count; k < 92; k++)
queenPlaces[k][ithQueen] = i + 1;
//设置控制范围
for(k = 0; k < 8; k++)
for(r = 0; r < 8; r++)
if(board[k][r] == -1 && (k == i || r == ithQueen || abs(k - i) == abs(r - ithQueen)))
board[k][r] = ithQueen;


/*调试程序
for(k = 0; k < 8; k++){
for(r = 0; r < 8; r++)
printf("%3d",board[k][r]);
printf("\n");
}
system("pause");
system("cls");
*/


//向下级递归
putQueen(ithQueen + 1);
//回溯,撤销控制范围
for(k = 0; k < 8; k++)
for(r = 0; r < 8; r++)
if(board[k][r] == ithQueen) board[k][r] = -1;


/*调试程序
for(k = 0; k < 8; k++){
for(r = 0; r < 8; r++)
printf("%3d",board[k][r]);
printf("\n");
}
system("pause");
system("cls");
*/


}
}
}

2011年11月30日星期三

百练 2816 红与黑 递归水题


这题貌似搞了好一阵子。。。囧~明明是很简单的题目好不好。。。。
代码能力还是太差。。。好像还做复杂了点 = =!!
或者不需要另外开一个int数组。。。不过算了 = =
本来认为应该是DP的。。。其实感觉DP的思想也是可以的吧。。。
开始的时候没有考虑到四个角的情况,走到边上就return了 = =
后来发现不行,就增大了数组,每条边外面都加一行吧。。。
越看越觉得傻X了。。。囧~





#include“cstdio”
int num[25][25];
char table[25][25];
int x,y;
void get(int a,int b)
{
num[a][b] = 1;    //标记
if(a < 1 || b < 1 || a > y || b > x)return;
if(table[a-1][b] == '.' && num[a-1][b] != 1)get(a - 1,b);
if(table[a+1][b] == '.' && num[a+1][b] != 1)get(a + 1,b);
if(table[a][b-1] == '.' && num[a][b-1] != 1)get(a,b - 1);
if(table[a][b+1] == '.' && num[a][b+1] != 1)get(a,b + 1);
}
int main()
{
int t1,t2,t = 0;
while(scanf("%d%d",&x,&y) && x){
for(int i = 1;i <= y;i ++)
scanf("%s",&table[i][1]);    //输入数据,从[1]开始。。。
for(int i = 1;i <= y;i ++)
for(int j = 1;j <= x;j ++){
num[i][j] = 0;
if(table[i][j] == '@'){t1 = i;t2 = j;}    //清零顺便记录起始位置
}
get(t1,t2);
for(int i = 1;i <= y;i ++)
for(int j = 1;j <= x;j ++)
if(num[i][j])t++;
printf("%d\n",t);
t = 0;
}
return 0;
}


标程就是标程 = =!!
/*
int f(int x, int y){
if(x < 0 || x >= W || y < 0 || y >= H)return 0; // 如果走出矩阵范围
if(z[x][y] == '#')return 0;
else{
z[x][y] = '#'; // 将走过的瓷砖做标记
return 1 + f(x - 1, y) + f(x + 1, y) + f(x, y - 1) + f(x, y + 1);
}
}
*/


POJ 1664 放苹果 递归水题

开始不知道处理M个苹果放N个碟子没有空的问题,和同学讨论了一下,这样可以理解为先在每个碟子上放一个苹果,这样就剩下M - N个苹果,可以在N个碟子上任意放。。。递归了 = =
想清楚了就真的水了。。。。囧~
所以说有时候递归的思路真的蛮清晰的= =!!



#include“cstdio”
int f(int a,int b)
{
if(b == 1 || a == 0)return 1;     //注意a为0的情况
if(b == 2)return 1 + a / 2;    //这里优化了一下 = =
if(a < b)return f(a,a);          //这里wa了一次,无语 = =
return f(a,b - 1) + f(a - b,b);    //分两种情况,有空盘和没有空盘的
}
int main()
{
int n,x,y;
scanf("%d",&n);
while(n --){
scanf("%d%d",&x,&y);
printf("%d\n",f(x,y));
}
return 0;
}

2011年11月27日星期日

百练 2694 逆波兰表达式 递归

这道题确实很能启发递归的思路啊。。。
但还是要吐槽下百练OJ,明明上面提示人家用cmath,结果G++不认介个。。CE = =
然后改cstdlib过了 = =
难道要用GCC? = =!!

#include"cstdio"
#include"cstdlib"
double func()
{
char s[20];
double a,b;
scanf("%s",s);
switch(s[0]){
case '+': return func() + func();
case '-': return func() - func();
case '*': return func() * func();
case '/': a = func(),b = func();
if(b) return a / b;
else return -1;    //这里return什么好。。。好像-1也不妥= =
default : return atof(s);
}
}
int main()
{
printf("%lf\n",func());
}

关于子函数的一些思考

前天和海天说起关于编程能力的问题。其实对于编程这东西,从来都是熟能生巧。自从开始做题以后,才慢慢发掘编程基本功真的好重要。简单的说,就是一种思维,或者说是条件反射。当你遇到一个问题,你很自然的会将之细分化,分隔成若干个过程,也就是子函数。这样你就只需要关心子函数的参数输入和返回值。对于整个main函数的流程构成是相当有帮助的。即使是debug的时候也可以很有针对性的进行调试。思路也要比之前只有一个主函数的时候清晰很多。
可以说,子函数就是一种解决问题的思想。对于如何快速的写出各种子函数,这就是考验编程能力的时候了。而main函数则是整个问题的基本思路。这样一道题的代码敲出来就要好看很多了。。。。
个人感觉这里面是有点面向对象的思想....不过想想也是,对象调用的成员函数不正是子函数的一种么? = =

百练 2755 二叉树 递归水题


常规思路 = =
直接打表出来,两个for找到最先相同的值。。。


#include“cstdio”
int a[20],b[20];
int main()
{
int x,y;
scanf("%d%d",&x,&y);
for(int i = 0;x + y;i ++){
if(x){a[i] = x;x /= 2;}
if(y){b[i] = y;y /= 2;}
}
for(int i = 0;a[i];i ++)
for(int j = 0;b[j];j ++)
if(a[i] == b[j]){
printf("%d\n",a[i]);
return 0;
}
return 0;
}




递归思路:



#include“cstdio”
int common(int x,int y)
{
if(x == y)return x;
if(x > y)return common(x / 2,y);
return common(x,y / 2);
}
int main()
{
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",common(a,b));
return 0;
}




数据太弱的情况,时间上看不出什么分别,但貌似递归的思路是比较巧妙的。。。。