Showing posts with label program. Show all posts
Showing posts with label program. Show all posts

Thursday, April 23, 2009

马桶 VS Outlook

看到一遍有意思的博文 《最差的产品是马桶还是Outlook》

首先,Outlook绝对不是最差的。
但是,如果要说最好的产品,马桶绝对名列前茅!

呵呵,有空得去向马桶学习一下用户体验。。

Tuesday, February 03, 2009

Simple Bulge Effect

Quite simple... Just add an equation before projecting the vertex position.

OUT.Position = IN.Position + ScaleFactor*0.5*(sin(IN.Position.y*Frequency*Time)+1) * IN.Normal.xyz;

The effect result is so so, but it's really interesting.


Another two animation effects in Cg Tutorial requires host application support, so I don't wanna study it now.

Sunday, February 01, 2009

Lighting Effect (Phong Model)

My implematation of Phong Lighting Model, refer to  Cg Tutorial Chapter 5, composed in FxComposer.

Below three pictures are captured in FxComposer. The Light is located at left-front-below position from the elephant, Light's direction to directly toward the elephant.
  1. Point Light, No Light Decay.

  2. Point Light, with Light Decay. The plane behind elephant is darker than front ones , as the Light is affected by distance between Light position and Vertex position. Refer to "Distance Attenuation" section of the tutorial.

  3. Spot Light, with Light Decay. Refer to "Adding a SpotLight Effect" section. This effect is more interesting than above two :)

Wednesday, January 21, 2009

Double Vision Effect

Just to congradulate my baby step of nVidia.Cg Effect composed in FX Composer.

No Effect -

Double Vision -


Get Source Code Here, this cgfx require Quad.cgh.

Wednesday, December 24, 2008

Mosaic Effect

Develop with RenderMonkey, refer to Someone's blog:

    

Get Source Code Here. (Not understand how to make RenderTarget work in FX Composor, so I just provide RenderMonkey project file here).
 

Wednesday, November 26, 2008

Blur and some Effects

Develop with RenderMonkey, refer to <Shaders for Game Programmers and Artists>:

  
 

Blur - Get Source Code Here.
EdgeDetection - Get Source Code Here.
Sharpening - Get Source Code Here.
Animated Texture - Get Source Code Here.
Motion Blur - Get Source Code Here. (not passed FX Composer Test, require fixing)

Tuesday, November 25, 2008

Tuesday, June 05, 2007

Stoer-Wagner算法

Stoer-Wagner算法是用来计算无向图的全局最小割的,理论上复杂度可以为O(|E|+|V|log|V|)。我的实现不是最好的,但觉得还行吧。

网友的好文章:最小割Stoer-Wagner算法

题目是百度06年的复赛题,星球大战。可以在POJ上练习,POJ2914。

相关论文和偶的代码,可点此下载

Stoer-Wagner算法的实现:


#include <iostream>
#include <algorithm>
using namespace std;

#define initSet(n,Arr) for(int i=0;i<n;++i)Arr[i]=i;
#define MAX 1<<30;
int graph[600][600];

// Stoer-Wagner Algorithm
int globalMinCut(int n){
// A is A set for Stoer-Wagner Algorithm
bool* A=new bool[n];
// V is vertex index
int* V=new int[n];
int* W=new int[n];

initSet(n,V);

int best=MAX;
while(n>1){

//the most tightly connected vertex.
int maxj=1;

// initialize set A and other vertex's weight
A[V[0]] = true;
for(int i=1; i<n; ++i){
A[V[i]]=false;
W[i]=graph[V[0]][V[i]];

if(W[i]>W[maxj])
maxj=i;
}

// find a min-cut
int prev=0,buf=n;
while(--buf){
// add it to A
A[V[maxj]]=true;

if(buf==1){
// update min cut
best=min(best,W[maxj]);

// merge prev and last vertex
for(int k=0; k<n; ++k)
graph[V[k]][V[prev]]=(graph[V[prev]][V[k]]
+=graph[V[maxj]][V[k]]);
V[maxj]=V[--n];
}
prev=maxj;
maxj=-1;

// update the weights
for(int j=1; j<n; ++j)
if(!A[V[j]]){
W[j]+=graph[V[prev]][V[j]];

if(maxj<0 || W[j]>W[maxj])
maxj=j;
}
}
}

delete[] A;
delete[] V;
delete[] W;
return best;
}


int main(){
// n - vertex number
// m - edge number
int n,m;

while(scanf("%d %d",&n,&m)==2){
memset(graph,0,sizeof(graph)/sizeof(bool));

// v-w is an edge with c weight
int v,w,c;

while(m--){
scanf("%d %d %d",&v,&w,&c);
graph[v][w]+=c;
graph[w][v]+=c;
}

// output min cut
printf("%d\n",globalMinCut(n));
}
}


Thursday, April 19, 2007

C++学习

发现很多公司要求C++,虽然工作已经有一个了。但7月上班,实在难耐,所以最近想着试几家大一点的公司,看看有没有更好的机会。由于强项在Java,很多大公司都只能望而却步。为了第一份工作一搏!决定重拾C++,发一帖学习日记,一是标出一些重点、难懂的东东;二是记录学习历程;三是可以给google友们多一个找答案的选择。
学习的书是问陈胖子借的,虽然不喜欢中文书,但听说翻译还可以,而且赶时间(就两个月了呀),呵呵,就不计较了。工具是VS2005,-_-b,可能要被小青年鄙视了,因为我整天就唠叨不要用VS,要用GCC。但确实,就业需求所逼,改吧。

Question 1: const
const很复杂,跟指针和引用混在一起更复杂。先两个概念,常量指针和指常量的指针:
int a=0;
const int *p1=&a; //指常量的指针,此处a可以是常量也可以不是,但结果是(*p1)不能再修改
int *const p2=&a; //常量指针,不可以改变p的指向,功能类似引用(但有区别)
const int *const p3=&a; //指常量的常量指针,上面两个特点综合一下

谈到指针和引用,说一下区别,引用是引对象,而指针就是地址。举些例子:
double a=0.0;
const int &b=a; //其实是先 int tmp=a;然后 const int &b=tmp; 因为b要引一个int对象,所以临时创建了一个
const int &c=0; //其实是先 int tmp=0;然后 const int &c=tmp; 同上,先创建一个对象


常量只可以用指常量的指针指。另有书上一例:
int *const &p=a; // 经检验,效果等同 int *const p=a;


Question 2: 陌生关键字
volatile : 表示变量是易变的,Java中也有。但C++中的含义是,编译器不能随意对该变量进行优化处理。即变量在编译器无法察觉的情况下可能改变。
sizeof : sizeof本身并不陌生,只上他有一点比较特殊,,sizeof是编译时刻计算的,因此被看作是常量表达式。


Question 3: 常用STL类型
vector 首当其冲,最好的替代数组的类型。有数组式的使用习惯和STL式的使用习惯,不能随意互用啊:
// 数组习惯
vector<int> vct(10);
for(int i=0 ; i<vct.size() ; ++i)
vct[i]=i;

// STL 习惯
vector<int> vct;
for(int i=0 ; i<10; ++i)
vct.push_back(i);
for(vector<int>::iterator iter=vct.begin() ; iter!=vct.end() ; ++iter)
cout<< *iter << endl;

complex 复数类型,可以用complex<double> 或者 complex<long double>等。
pair 关联两种类型的值。
bitset 向量类型,用 bitset<32>构造向量长度为32。
list,deque,map/multimap, set/multiset
stack 栈,其中与一般栈不同之处是普通栈的 pop删除头并返回头元素,而stl栈只删除不返回,它使用top返回头。
queue和priority_queue,队列和优先队列,priority_queue在Java下满好用的 ^_^。

Question 4: 自己写类
写一个完整的类不容易啊,首先分为三个基本步骤(自己总结的,不知道对不对):
1 类定义,常定义在.h文件中
2 函数、变量定义,.cpp文件的部分
3 添加其他会使用到该类的其他类中的方法,典型的如 istream& operator >>( istream&,类&) 等。

在定义时也能定义部分类函数和变量,在类定义中定义的函数的都是缺省为inline的(使用频率高的,短小的代码可以考虑声明为inline)。在类外定义的需要显示声明inline。

类定义基本结构:

#include <iostream> //包含需要使用的头文件

class Sample; // 先声明一下类

// 声明一些会使用到该类的函数
istream& operator >( istream&, Sample&amp;);

// 类定义
class Sample
{
public:
Sample(); //默认构造函数及其他构造函数
Sample( const Sample&); //拷贝构造函数,如果需要

~Sample(); //析构

Sample& operator = (const Sample&amp;); //重载操作符

void setValue(int); //定义成员函数
private:
int _value; //定义成员变量
};

Question 5: 显式转换
新式(标准C++)强制类型转换: static_cast,dynamic_cast,const_cast,reinterpret_cast
任何非const数据类型的指针都可以被赋值给void* 指针。
const_cast< type >( expression ) 用于转换掉表达式的常量性 (以及volatile对象的volatile性)。
static_cast< type >( expression ) 编译器隐式执行的任何类型转换都可以由它显式完成。
reinterpret_cast< type >( expression) 对于操作数的位模式执行一个比较低层次的重新解释,它的正确性很大程度上依赖于程序员的主动管理。
dynamic_cast<type>( expression )支持运行时刻识别由指针或引用指向的对象。


旧式(标准C++前)强制类型转换:可以替代static_cast、const_cast、reinterpret_cast。

Question 6: 容器
抽象容器类型{
顺序容器(sequence container): list,vector,deque
关联容器(associative container):map,set
}

每个容器支持一组关系操作符,可以用来比较两个容器。第一个不相等元素的比较决定了两个容器的大小关系。
容器的类型有三个限制
  • 元素类型必须支持等于操作符
  • 元素类型必须支持小于操作符
  • 元素类型必须支持一个缺省值
所有预定义数据类型,包括指针,都满足这些限制,C++标准库给出的所有类型也一样。

Question 6: 迭代器
除了iterator类型,还定义了一个const_iterator类型,后者对于遍历const容器是必需的。 iterator算术运算只适用于vector或deque,而不适用于list。

Question 7: 函数
参数为数组,数组确实满麻烦的,首先数组参数绝不会传值,传的是指针。举以下五个例子,前三者相同:

void putValues(int*);
void putValues(int[]);
void putValues(int[ 10 ]);
void putValues(int (&arr)[10]); //参数为10个int的数组,因为参数是引用的,即数组长度成为参数的一部分
void putValues(int matrix[][10], int rowSize); // 第一维长度为rowSize,第二维长度是10
void putValues( vector<int> &vec ); //vector是替代数组的最好类型


指定函数的缺省实参:

int func1(int a,int b=0,int c=0);

int func2(int a,int b,int c=0); // 初始化最右边参数
int func2(int a,int b=0,int c); //可以!此时b c都有缺省参数
int func2(int a,int b=0,int c=0); //错误,bc已经有缺省参数

int func3(int a,int b,int c=func1(1)); //缺省实参可以是任意表达式

int func4(...); // ellipsis 省略号,告知编译器,函数调用时,可以有0或多个实参,而类型未知


命名返回值优化,易犯错误:
1 返回一个指向局部对象的引用。局部对象的生命周期随函数的结束而结束。

Matrix& func()
{
Matrix a;
return a; // 错误,结果将指向一个错误的位置
}



2 函数返回一个左值,对返回值的任何修改都将改变被返回的实际对象。

int& get_val( vector<int> &vi, int ix)
{
return vi[ix];
}

int ai[4] = {0,1,2,3};

vector vec(ai, ai+4);

int main()
{
get_val(vec,0)++;
}


正确使用命名返回值优化:

Matrix& get(Matrix *p)
{
Matrix *res = new Matrix(); //动态分配的,函数结束不会结束
*res=*p;
return *res;
}

函数指针(指向函数的指针类型):

int cmp1(const string &s1, const string &s2)
{
return 1;
}

int cmp2(const string &s1, const string &s2)
{
return 0;
}

int *pf(const string&, const string&amp;amp;amp;amp;amp;amp;); // 错误,返回类型指针
int (*pv)(const string&, const string&amp;amp;amp;amp;amp;amp;) = 0; //初始化,不指向任何东西
int (*pf)(const string&, const string&amp;amp;amp;amp;amp;amp;) = cmp1; //正确,pf 是指向函数的指针
pf=cmp2; //赋值,可以
pf=&cmp1; //可以,同上

cmp1("a","b");
pf("a","b");
(*pf)("a","b"); //三个效果相同,返回1


函数指针的数组(so...神奇):

int func1(const int &a,const int &amp;amp;amp;amp;amp;amp;b);
int func2(const int &a,const int &amp;amp;amp;amp;amp;amp;b);

typedef int (*PFV)(); //定义函数类型指针的typedef
PFV tc1[10]; // 函数指针数组长度为10
PFV tc2 = { func1, func2 }; // 初始化
tc1[0] = &func1; // 赋值
tc1[0](10,20); //调用
((*tc1)[0])(10,20); // 显式调用


函数指针可以作为返回类型,但是函数不能做为返回类型:

int (*func(int))(int, int); // ff为函数,有一个int参数,返回一个指向函数的指针,该指针类型为 int (*)(int, int);

typedef int (*PF)(int, int);
PF ff( int ); //更优雅

typedef int func(int, int);
func ff(int); // 错误,返回类型不能是函数类型



Question 8: extern
链接指示符 extern "C" / extern "Ada" / extern "FORTRAN"
告诉编译器,该函数是使用其他语言编写的:

extern "C" void exit(int);

extern "C"
{
int printf( const char* ...);
int scanf(const char* ...);
}

extern "C"
{
#include <cmath>
}

Question 9: 头文件
头文件为所有extern对象声明、函数声明以及inline函数定义提供了一个集中的位置。
预编译头文件而不是普通头文件可以大大降低应用程序的编译时间。
头文件不应该含有非inline函数或对象的定义。否则将可能使同一程序的两个或多个文件中包含,就会产生重复定义的编译错误。


Question 10: 域和生命期
局部对象:自动对象、寄存器对象、局部静态对象

自动对象地址不应该被用作函数的返回值,因为函数一旦结束了,该地址就指向一个无效的存储区。当一个自动变量的地址被存储在一个生命周期长于它的指针时,该指针被称为空悬指针(dangling pointer)。

寄存器自动对象:在函数中频繁被使用的自动变量可以用register声明。

for( register int ix=0; ix


静态局部对象:未初始化的静态局部对象会被程序自动初始化为0,相反,自动对象的值是任意的,除非它被显式初始化。

动态分配的对象:空闲存储区被耗尽时,new表达式失败,所以抛出一个bad_alloc异常。delete会调用操作符delete(), C++保证若指针被设置为0,则不会调用delete(),故之前没必要测试指针是否为0。但delete之后,并不会把指针赋值为0。

auto_ptr
auto_ptr可以帮助程序员自动管理用new表达式动态分配的单个对象,不支持数组。


#include
auto_ptr< type_pointed_to > identifier( ptr_allocated_bynew );
auto_ptr< type_pointed_to > identifier( auto_ptr_of_same_type );
auto_ptr< type_pointed_to > identifier;

auto_ptr< int > pi(new int(1024));

由于操作支持都是内联的,所以效率不比直接使用指针代价高。ptr相关操作:
reset: 初始化后,就只能用reset再对auto_ptr赋值了。
get: 返回auto_ptr对象内部的底层指针。
release:允许将一个auto_ptr对象的底层对象初始化或赋值给第二个对象,而不会使两个auto_ptr对象同时拥有同一对象的所有权。

定位new表达式:

new ( place_address ) type-specifier // 表达式格式

class Foo{};
char *buf = new char [ 1024 ];
Foo *pb = new ( buf ) Foo; //无须delete
delete[] buf;

Thursday, March 01, 2007

Widget for Google Calendar

See my blog's left bar, there is a fantastic widget that shows my google calendars in the form of event list and calendar table.

Now, the widget is available here:
http://ray58750034.googlepages.com/googlecalendarwidget

Reply your comments, ideas and the bugs you find about the widget here, thx.

Friday, February 23, 2007

Graphics program with Javascript

Apple introduced a new HTML tag <canvas> to the world, which endue client-side script language the ability of graphics drawing. Now, Firefox and Opera has implement the <canvas> tag, so the javascript programmer was able to write a rich graphic-based client application.

I've written a simple ball game to show the interactive between javascript graphic app and browser user.

See details : http://ray58750034.googlepages.com/javascriptballgame
See more examples : http://developer.mozilla.org/en/docs/Category:Canvas_examples

Friday, February 16, 2007

Javascript Menu 代码

只为了显现一个简单的menu,网上搜了不少代码。 但要么就是太复杂,搞得我改都无从改,要么就是不符合我的需求。结果自己静下心 20分钟就搞定了, -_-b ,找了大概2小时啊,汗呐。

非常简单的要求:
  1. 必须是js代码,因为我要实现menu在很多页面里都要用,如果掺了html就要用iframe之类的,iframe毕竟不舒服啊。
  2. 对界面没什么要求,简单的ul和li就行了,css沿用站点的,无须另外配。
  3. 多级目录结构
  4. 每层menu可以是链接,也可以是打开子目录
  5. 可复用,其实就是实现类似jsp的<%@include file="">的作用,只是我没有服务器,所以只好用js啦。
先看效果:



以下是我的代码:

<div id="soya_menus">
</div>
<script language="JavaScript">

var MENU_ITEMS = [
['Ray',{ href : 'http://raythking.blogspot.com/',target : "_blank" }],
['Google',null,[
['Web',{ href : 'http://www.google.com',target : "_blank"}],
['Image',{ href : 'http://images.google.com',target : "_blank"}],
['Video',{ href : 'http://video.google.com',target : "_blank"}]
]
],
['Baidu', null,[
['Index',{ href : 'http://www.baidu.com/',target : "_blank"}],
['Zhidao',{ href : 'http://zhidao.baidu.com/',target : "_blank"}]
]
],
['Sogou',{ href : 'http://www.sogou.com/',target : "_blank"}],
['About',{ href : 'javascript:alert(" This is a simple javascript menu !")',target : "_self"}]
];


function showSubMenu( tolist ){
var sublist = document.getElementById(tolist);

if(sublist.style.display == ""){
sublist.style.display = "none";
}else{
sublist.style.display = "";
}
}

function insertMenu(divarea){
document.getElementById(divarea).innerHTML=createMenu(MENU_ITEMS,"soya_menu");
showSubMenu("soya_menu");
}

function createMenu(menu_it,id_pre){
var html="<ul id='"+id_pre+"' style='display:none;padding-top:8px' >";
for( var i=0 ; i<menu_it.length; ++i){
var sub_id_pre=id_pre+"m"+i;
html += "<li>";
if(menu_it[i][1]){
html += "<a href='"+menu_it[i][1]+"'>";
html += menu_it[i][0];
html += "</a>";
}else if(menu_it[i][2]){
html += "<a href='"+menu_it[i][1].href+"' target='"+menu_it[i][1].target+"'>";
html += menu_it[i][0];
html += "</a>";
html += createMenu(menu_it[i][2],sub_id_pre);
}else{
html += menu_it[i][0];
}

html += "</li>";
}
html +="</ul>";

return html;
}

insertMenu("soya_menus");

</script>

Wednesday, February 14, 2007

不同浏览器下装载js的不同表现

这两天在改blog,因为脑子里又掺了一些杂七杂八的念头,想要实现一下,呵呵。如果这两天有看我blog的话,可能时不时会发觉自动跳框啊或者某个功能怎么没了等等状况。那就是我在改blog,这个时候要找我的话,直接上gtalk就行啦。

为了实现多个浏览器下效果一致,花了不少心血啊。先看看效果吧,见左边的“日程更新”,我的Calendar是以event list形式表示,当然可以有其他选择,这是后话(有空时候再说吧)如果是Firefox的话可以看到边是带弧的,是用的css3,ie和opera下没看出来。ie下还有其他一些css效果也看不出来。不过基本的效果是三个都一样的。

因为涉及要下多个Calendar然后合并的操作,发觉三个浏览器对于js下载和加载的表现各不相同。在做Web可要特别注意了哦,在这我说一下我遇到的情况吧:

情况如下,首先要下载四个Calendar的数据(用Google 的 JSON API),每个下载完回调一个Handle,Handle将数据加入EventList。四个都Handle过了,调用showEventList()。三个浏览器的下载方式应该都是并发的,这点应该没有疑问。每一个Calendar的操作相当于是在一句<script src="****"></script>,加载顺序正好三种浏览器三种方式。


首先是IE,由于对多个Calendar操作,然后用到了一个eventlist,可以想象是多线程在操作一个数据域。IE可以说是完全多线程的,如果给Calendar编号 1 2 3 4,那么在下载完数据后回调的顺序可能是 2 3 1 4 或者 1 3 4 2 等等。另外,用来动态加载这些javascript的那段js与这四个新js也是并发的。这样有两点麻烦:
  1. 时间上难掌握:程序是非顺序的,对于需要顺序完成的任务实现时要加不少辅助变量。
  2. 空间上易出错:由于好几个Calendar的Handle是同一个Handle,这样就要手动控制好变量的同步,容易出现脏数据。

然后是Opera,Opera不论从哪方面讲都适合年轻人使用,充满了朝气!今天的修改其实主要是针对IE的,Firefox和Opera基本上是一致的。但在最后,Opera的一个反常举动让我调了老半天才调出来。
Opera的js装载好象是完全顺序,与IE截然相反。顺序到一个页面的所有script都得一步一步来,就像只有一个线程。如果以原script为 0,四个数据js文件还是 1 2 3 4,那么Opera的执行顺序将是 0 1 2 3 4 0,最后的0是返回原脚本,感觉就是堆栈式的函数调用。就是最后那个返回原脚本,让我措手不及。楞了老半天才发觉。


最后是Firefox,个人觉得,Firefox 更适合用来开发 web 程序。就在今天的修改过程中,无论我怎么改,只要基本的逻辑是对的,那么Firefox的效果始终是如预期的。
以Opera那种假设下来说,Firefox的执行顺序是 0 1 2 3 4。我觉得这样理解好了,Firefox是把script放在页面文件中。装载顺序是从头到尾依次装载的(严格按照js在页面中出现的顺序),js的运行不并发,装一个运行一个结束后再装。几个好处吧:
  1. 更贴近web开发者的正常逻辑。web开发者一般是写页面的时候嵌js,而不是写js的时候嵌页面 。这样Firefox显然比Opera更适合web开发者。
  2. 脚本并行处理(IE)考虑了一块js一个用途的方式时候的效率,但是不适合多个js联合工作,特别是需要时间上和空间上控制的工作。

其实单就用户体验来说,IE可能更受欢迎,毕竟是多线程的,不会因为一个js的错误而导致整个页面僵死。而且就三个浏览器看我blog的速度来说,IE应该是比较快的(除了Opera在有缓存的情况,其缓存给我的印象颇深刻~)。但我还是比较喜欢Firefox,毕竟咱是做“写”工作的人嘛,而且速度这个是可以用不同手段提高的。

最后赞一下Firebug,开发人员的福音啊!

Saturday, February 10, 2007

API地图应用大赛

51地图举办的地图API比赛,API确实好用!赞一下51地图。
主要是看中了参与奖是一年的免费《程序员》,不过比赛要五月结束,准确地是说是半年多的《程序员》 -_-b 。

从吃完晚饭到现在,简单地实现了一个idea,先暂时搁在blog上。idea是从新闻里看到现在维修服务(包括小区物业、也包括马路边的公共设施等)讨论得特别多,什么水龙头漏水、空调坏啦、还有道路不方便等等都要报修,好的十几分钟就到,差的搁了几个月也没人管。

所以,我想是不是可以利用地图来做一个“我要修”(不是我要秀)的网站。服务流程如下:
  1. 客户端提供报修,报修人必须提供身份认证,不能让我们的维修工人白忙活,要保证可信度。(认证方法可以替换掉我的sample的身份确认那个输入框,可以用手机号码做为输入)
  2. 身份认证后,报修人标明地点、填写报修详细。标明,可以是输入地点自动标明(比如输入东方明珠,地图可以自动标到东方明珠),也可以手动标记(手动标记就不显示地点名了,改成坐标)。
  3. 确认填写信息无误,发送给服务器端。
  4. 报修中心可以固定时间来看报修情况(比如在人员有充沛的时候),然后选择距离该中心近的点派职工去维修。
  5. 对于报修成功的用户给以虚拟奖励......这是后话。
我这粗略地实现了客户端的地图(当然是用51的API啦),后台是没有服务器端的,毕竟我这只是blog嘛:


自动标记地点

(请输入证件号以便身份确认)



打开城市地图 打开手动标记 提交报修申请




Saturday, January 27, 2007

蜜蜂的问题

老blog里比较不舍得丢的文章,移到这里来:

99ACM/ICPC final有一道蜜蜂的坐标的问题,非常有意思,和队友们讨论了许久都没能解决。记得那天做练习是我和肖筹划着用计算几何的方法估算出结果,虽然做法很独特,但毕竟是估算的,最后没有能过。后来我找了份解题报告,看到了解这类问题神奇地使用笛卡儿坐标,着实让人兴奋啊。所以准备了一份模板、数道类似问题和将蜜蜂坐标放在扫雷游戏里的应用,来一个蜜蜂问题的大汇总。


模板放在SHU ACM群里,含有一些处理正六边形拼成的坐标系的方法。

首先,蜂巢是由许多个正六边行无缝拼接而成的,这是一个美丽而奇特的自然结构,如图(图中程序是在解一道题时写的辅助程序,我也放在群与大家分享):


题外话,先来讨论下为什么六边形可以无缝的拼接出一个平面,那么七边、八边行不行?其实是一个简单的小学题: 正n边形内角为 (n-2)*180/n ,要保证可以无缝拼接,就是一个圆可以被整数个n边形内角拼接,即 360=k*(n-2)*180/n => 2n=k(n-2),用这个公式就能算一下是否正n边形能像六边行一样拼出一个平面了。


起初在遇到这样独特的结构时可能无法想象如何将其保存起来,可能会想到是不是做一个带六条链结点的链图?这样做的结果将是一个极难维护的链图,光是想到插入操作就起鸡皮疙瘩。


有一个十分巧妙的方法来储存这个怪异的结构:用二维数组。用二维数组保存四个方向或八个方向的东西肯定是很简单的,其实对于这个六向的结构,我们可以取二维数组的八向中的六向。即东、南、西、北、东北、西南(其实只要注意对称,六向可以随便取),这样相对的坐标向每个方向移动的偏移量就是:

西 东北 西南
x 1 0 -1 0 1 -1
y 0 -1 0 1 1 -1

到底正确与否呢,其实是必然正确的,只是我们损失了两个角(东南、西北),不过不碍事。通过上面这个方法不仅是保存了六边形的怪结构,而且也给这种结构指明了一个坐标表示的方法,在解题中这个概念很重要。


PKU 1134

与蜜蜂坐标没关系的题目,只是样子是六边行的。题目意思是给出三行,每行代表一个方向,然后可以按任意顺序把数字放在这个方向的任意一列中(以abcba的顺序分配到abc里)。归纳一下可以得到以下结论, 最大的得分就是三行分别乘以下面的系数:

1a * 8 + b * 6 + c * 5
2a * 7 + b * 7 + c * 5
3a * 7 + b * 7 + c * 5 。所以枚举一下就可以求出最大值了。


PKU 2265

这是非常简单、易上手的题目,熟悉两种坐标系对于解决蜜蜂问题很有帮助,这两个坐标系也是蜜蜂最常用的坐标,转换的方法就是直接上模板。


PKU 1870

99FINAL的题目,除了用模板里的构造点来构造一个六边形结点(带XY轴坐标)以外,模板里有求两点间最短距离的函数。


求最短距离要以一点为原点,即用一点减去另一点的XY坐标。然后对相对的那个点的两种情况做处理:xy同正同负时,距离是max(|x|,|y|);否则距离为距离是|x-y|。简单得想象一下就知道了。


PKU 1792

1870的升级版,除了要求最短的距离外,还需要求两个点间有多少种路径可以用最短的距离。其实求可能的路径数可以归约为求组合数的问题:


上图为例,在x>0 y>0的区域,最短距离是max(|x|,|y|),图中某点的最短距离很明显是 |x|,然后如何求路径数呢?这样思考,为了能到达那个点,所有路径在两点之间准没错吧(出了两点间的区域就不能最短距离了)。那在最短路径为d的情况下,取d中的一点让它或上或下(只要保证还在两点间的那块区域),不就可以得到一条新的路径?这样,在d中取c个点(c为最大的可变的点,或称关节),就是总路径数。那么总的路径数就是combine(d,c)了。巧合的是c的值情况与最短路径的值情况正好相反,即xy同正同负时,c|x-y|;否则cmax(|x|,|y|)

模板里有个函数实现此功能,不过自己写写也很简单的吧。


PKU 3036

哈,在1792有点类似,不过做法完全不一样的题。求的是从原点出发到达距离为n的地方再回来的路径数。其实知道用动态规划和前面讲的六个方向的坐标的话,这道题目是非常简单的。动规方程见下:


dp[i][x][y] i表示距离原点i步;xy是点的坐标。

 dp[i][x][y]=  dp[i-1][x+1][y] + dp[i-1][x-1][y] + dp[i-1][x][y+1] +dp[i-1][x][y-1] + dp[i-1][x-1][y-1] + dp[i-1][x+1][y+1]
初始dp[0][x][y]都为0dp[0]原点为1。然后开始dp,最后dp[i]原点就是第i步的解。

上面的是比较简单的或基于六向的坐标系的,下面两道比较有意思,是对于此类坐标系的扩充:


PKU 1957

那个辅助程序就是我为了做这道题目而写的,用法是先输入walk,然后按lrotate可左转(正规化过)。

题目的解法很容易想到的,就是先将走过的点放在一个set里,然后做旋转和正规化,直到两个set的元素都相等为止。

依照行走的路线构造点不是件难事,从原点出发。旋转是比较费神的,正规化的话其实就是找出一个最左最右的点作为参照点,所有点减掉它就行了。

旋转可以从两个角度考虑,一是考虑求出了点之后做旋转,二是考虑在将行走路线做旋转然后求点。如果从前种方向考虑的话,会很累,在简单的四向的坐标系中点相对于原点的转动可以通过乘以一个变换矩阵得到,而对于一个六向的坐标系,举个例子:

一个二维数组保存的七个点(O表示该位置无值,X表示该位置是一个六边形结点):

O X X               X X O
X X X ===========> X X X
X X O 乘以变换矩阵 O X X

很明显,旋转后的不是原来使用的六向的坐标表示方法了。当然,调整变换矩阵的话,相信还是有办法解决的,但这毕竟是个较麻烦的活儿。

那么从后者的方向来考虑的话,事情就简单了很多。见下图:

按行走路线进行转换的思想就是上图所表现的,显而易见了吧。代码实现也是相当的简单。即将把原来的a方向的操作改为b方向操作等等。

这样的话,先输入第一种走法,正规化一下。然后输入第二种,做七次旋转(包括不转的那种),每次正规化后再比对两个集合就行了。

PKU 1518

看上去很吓人的题目,跟计算几何又有关系,而且坐标系又是要六向的表示。Take it easy,先来看看一个扫雷游戏吧:


在扫雷游戏中有这样一个问题,鼠标点击了之后要出发事件,事件的参数只有鼠标点击的点的坐标(x,y)。求鼠标点击的是哪块雷区,知道了哪块雷区才可以做相应操作嘛。


通过平面坐标求六边形的坐标的方法其实满多的,在这里介绍一个简单的方法:


首先,要判断点是否在六边形内,经常使用的是用叉乘判断点对于凸边形的每个顶点是左转还是右转可以做到。不过这里不需要这么做,因为六边行都是紧凑得在一起,而且都是正多边形,所以求一下点到每个多边形中心的距离,然后取最小值可以找到它所在的六边形了(如果正好在边上,随便取哪个都一样)。


为了优化一点,不用对每个六边形都求一下它的中心与目标点的距离。可以先从距离估算出目标点的大致位置,然后包括大致位置的六边形在内,相邻的六个六边形再确定出最终点所在的六边形。


估算的方法是简单的计算几何(d是六边形边长,tdsqrt(3)*d/2p是目标点):

x = (int) Math.round((p.x * 2 / (3 * d)));
y = (int) Math.round(((-p.y + x * td) / (2 * td)));

知道了六边行的坐标,接下来的事情就容易了。求两坐标间最短路径,然后路径乘以2*td。再将具体的两个点与六边形中心的距离相加。就是结果了,当然要注意在同一个六边形内的两点是直接计算两点距离。


链接:

Ray's Blog: http://raythking.blogspot.com/
SHU-ACM-Group: http://groups-beta.google.com/group/shu-acm-group
Mineray: https://mineray.dev.java.net/


Friday, January 26, 2007

Topcoder SRM336 DIV II

又回来做Topcoder了,用Ubuntu就是爽啊,又快又华丽,破Windows上Topcoder会有问题。最近Vista的漏洞频频被揭,微软信用大大受损,相反的,Solaris一方面与Intel达成战略合作,另一方面Solaris在争取PowerPC的营地。

学校的网络真是一团糟! 我只能找个HTTP代理,用HTTP的管道上topcoder。时不时就掉下线来。

比赛依然是紧张激烈的,这次是在DIV II 里,rating大概是房间里排第三 1157分。 三道满简单的题目,都是在时间内做出来的:
237.65/250 - 383.94/500 - 0.00/900
其中900的那题,我初次提交是580分好象,后来发现漏了一个case,急啊,赶紧在比赛结束前一分钟改过来(汗,真的是结束前一分钟啊,看着秒数动的,那个心跳啊),得292.85。

challenge阶段网络麻烦不断啊,每每打开别人的代码就会断线,浪费了好多时间。而challenge第一次失败了,虽然我找出了别人的bug,但数据没出好,第二次才成功挑战掉。 我challenge的是900的那道中score有重复的情况,这也是我后来发现自己没考虑到的一个case: {3,3,3} 1 4,如果不考虑重复会得结果 -1,但是正确结果是4。

System Test的最后结果是 900分的Failed,郁闷-_-b,不过还好,还是第三。现在rate 1231, -_-b 再接再历啊。

Monday, January 15, 2007

Widgets Or Gadgets... PK!

微软和yahoo两个超级流氓大户铆上了~~ (在CSDN上看到的)

http://microsoft.csdn.net/vista/gadgets-contest/index.aspx

首先,不知道是微软还是yahoo,或者另有其人,发明了在桌面上放小程序。于是,两家公司(有钱啊)往程序员身上砸,砸着砸着就出了两个小东西——widget and gadget

yahoo还是很有头脑的,举办了yahoo widget创意大赛,借此大肆宣传他的widget引擎(从大流氓软件转向小流氓软件)。
不过,微软更是流氓(偶觉得霸道已经不足以形容微软了)。。。。
yahoo widget刚刚结束一个月都不到。。。微软大兴土木地搞起了 Vista Gadget 比赛,比赛网站 做得跟yahoo widget的比赛网站神似~ -_-b
不知道yahoo可不可以凭这个去告微软 窃取yahoo的创意,但我想微软再大的官司都不怕。。。这种挠痒痒的怎么会放在眼里。



由于gadget只能在vista环境下运行,微软不仅是在宣传gadget这个小东西,更是为背后那个大阴谋作铺垫。。。。。。
醉翁之意不在酒,每个参赛者不光是给微软做义务宣传,和开源那套思想不同,做完了宣传,你还得掏钱去买他的系统。

虽然yahoo也不是省油的灯,但确实要为他捏把汗呐。。。。。
人家操作系统都捆绑了 gadget,yahoo的widget还怎么跟他斗。。。。
或者可以学学Netscape,先到linux下窝两年再说吧。。。

给个yahoo widget的主站,祝愿widget挺住:
http://cn.widget.yahoo.com/
最后鄙视一下学校考的微软认证.......一点含金量都没 -_-凸

Saturday, December 23, 2006

Flash ActionScript?

帮朋友写作业用到了Flash(我这人,哎,好说话呀)。

Flash的确强大,与Yahoo Widget不是一个级别的。
  • 动画制作的强大studio。
  • 提供表单应用程序所需的大部分组件。
  • 已经比较成熟的ActionScript脚本语言。
两天的功夫。。虽然之前从没用过,不过还是赶了出来。头一天跟着样例和感觉做、然后去网上查文档。 http://www.flashplayer.cn 有不少好东东。
制作的过程中,胖昕跑过来要我帮他做一个抽奖程序。。。汗,我又答应了,不过要求很简单,有个开始有个停就行了,也用flash一并做了。

作品还是八数码(因为太熟悉了,所以写得快),在此下载 http://www.tomore.com/dispdocnew.php?id=43057

Yahoo Widget?

yahoo widget是好东东(其实就是xml加上javascript,流行啊),yahoo widget比赛令我满不爽的 ( cn.widget.yahoo.com )。我的作品在第12-13页的RSS Cabint,成绩满惨的。总结一下教训,避免再做同样的傻事:
  1. 软件不合题意。题目要求创新,RSS管理软件没什么创新的。规则说是投票制,我的软件色彩颇暗,coder的风格,似乎不迎合大众的口味。
  2. 软件的市场定位不对。我不知道国人中有多少是在用RSS的,而我的更是使用了OPML来管理RSS,显然有些天方夜谭的味道。
  3. 宣传没做。关系网里没几个是适合来帮忙宣传这个的。尴尬啊,做人太死板了。要改要改。
虽然不适合比赛,但东西还是好东西。
http://www.tomore.com/dispdocnew.php?id=42885 有下载。

再给胖昕拉拉票,他的作品是八数码,满有希望拿奖的!祝好运!