随笔:78 文章:7 评论:38 引用:0
C++博客 首页 发新随笔
发新文章 联系 聚合管理

转自:http://blog.sina.com.cn/s/blog_4d035b080100k3ep.html?retcode=0
opengl文字处理(1) (入门学习十六)(转)

OpenGL
版的“Hello, World!”
写完了本课,我的感受是:显示文字很简单,显示文字很复杂。看似简单的功能,背后却隐藏了深不可测的玄机。
呵呵,别一开始就被吓住了,让我们先从“Hello, World!”开始。
前面已经说过了,要显示字符,就需要通过操作系统,把绘制字符的动作装到显示列表中,然后我们调用显示列表即可绘制字符。
假如我们要显示的文字全部是ASCII字符,则总共只有0127128种可能,因此可以预先把所有的字符分别装到对应的显示列表中,然后在需要时调用这些显示列表。
Windows系统中,可以使用wglUseFontBitmaps函数来批量的产生显示字符用的显示列表。函数有四个参数:
第一个参数是HDC,学过Windows GDI的朋友应该会熟悉这个。如果没有学过,那也没关系,只要知道调用wglGetCurrentDC函数,就可以得到一个HDC了。具体的情况可以看下面的代码。
第二个参数表示第一个要产生的字符,因为我们要产生0127的字符的显示列表,所以这里填0
第三个参数表示要产生字符的总个数,因为我们要产生0127的字符的显示列表,总共有128个字符,所以这里填128
第四个参数表示第一个字符所对应显示列表的编号。假如这里填1000,则第一个字符的绘制命令将被装到第1000号显示列表,第二个字符的绘制命令将被装到第1001号显示列表,依次类推。我们可以先用glGenLists申请128个连续的显示列表编号,然后把第一个显示列表编号填在这里。
还要说明一下,因为wglUseFontBitmapsWindows系统特有的函数,所以在使用前需要加入头文件:#include <windows.h>
现在让我们来看具体的代码:

#include <windows.h>

// ASCII
字符总共只有0127,一共128种字符
#define MAX_CHAR       128

void drawString(const char* str) {
    static int isFirstCall 1;
    static GLuint lists;

    if( isFirstCall // 
如果是第一次调用,执行初始化
                        // 为每一个ASCII字符产生一个显示列表
        isFirstCall 0;

        // 
申请MAX_CHAR个连续的显示列表编号
        lists glGenLists(MAX_CHAR);

        // 
把每个字符的绘制命令都装到对应的显示列表中
        wglUseFontBitmaps(wglGetCurrentDC(), 0, MAX_CHAR, lists);
    }
    // 
调用每个字符对应的显示列表,绘制每个字符
    for(; *str!='\0'; ++str)
        glCallList(lists *str);
}



显示列表一旦产生就一直存在(除非调用glDeleteLists销毁),所以我们只需要在第一次调用的时候初始化,以后就可以很方便的调用这些显示列表来绘制字符了。
绘制字符的时候,可以先用glColor*等指定颜色,然后用glRasterPos*指定位置,最后调用显示列表来绘制。

void display(void) {
    glClear(GL_COLOR_BUFFER_BIT);

    glColor3f(1.0f, 0.0f, 0.0f);
    glRasterPos2f(0.0f, 0.0f);
    drawString("Hello, World!");

    glutSwapBuffers();
}

效果如图:



指定字体
在产生显示列表前,Windows允许选择字体。
我做了一个selectFont函数来实现它,大家可以看看代码。

void selectFont(int size, int charset, const char* face) {
    HFONT hFont CreateFontA(size, 0, 0, 0, FW_MEDIUM, 0, 0, 0,
        charset, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
        DEFAULT_QUALITY, DEFAULT_PITCH FF_SWISS, face);
    HFONT hOldFont (HFONT)SelectObject(wglGetCurrentDC(), hFont);
    DeleteObject(hOldFont);
}

void display(void) {
    selectFont(48, ANSI_CHARSET, "Comic Sans MS");

    glClear(GL_COLOR_BUFFER_BIT);

    glColor3f(1.0f, 0.0f, 0.0f);
    glRasterPos2f(0.0f, 0.0f);
    drawString("Hello, World!");

    glutSwapBuffers();
}


最主要的部分就在于那个参数超多的CreateFont函数,学过Windows GDI的朋友应该不会陌生。没有学过GDI的朋友,有兴趣的话可以自己翻翻MSDN文档。这里我并不准备仔细讲这些参数了,下面的内容还多着呢:(
如果需要在自己的程序中选择字体的话,把selectFont函数抄下来,在调用glutCreateWindow之后、在调用wglUseFontBitmaps之前使用selectFont函数即可指定字体。函数的三个参数分别表示了字体大小、字符集(英文字体可以用ANSI_CHARSET,简体中文字体可以用GB2312_CHARSET,繁体中文字体可以用CHINESEBIG5_CHARSET,对于中文的Windows系统,也可以直接用DEFAULT_CHARSET表示默认字符集)、字体名称。
效果如图:



 

posted @ 2010-11-14 21:36 未央 阅读(704) | 评论 (0)编辑 收藏
 

转载:http://blog.csdn.net/xie_zi/archive/2007/12/09/1925778.aspx
GLUT教程              鼠标
在前几节,我们看了怎么使用GLUT的keyboard函数,来增加一个OpenGL程序的交互性。现在,是时候研究下鼠标了。GLUT的鼠标接口提供一些列的选项来增加鼠标的交互性。也就是检测鼠标单击,和鼠标移动。
 
检测鼠标Clicks
和键盘处理一样,GLUT为你的注册函数(也就是处理鼠标clicks事件的函数)提供了一个方法。函数glutMouseFunc,这个函数一般在程序初始化阶段被调用。函数原型如下:
void glutMouseFunc(void(*func)(int button,int state,int x,int y));
参数:
func:处理鼠标click事件的函数的函数名。
从上面可以看到到,处理鼠标click事件的函数,一定有4个参数。第一个参数表明哪个鼠标键被按下或松开,这个变量可以是下面的三个值中的一个:
GLUT_LEFT_BUTTON
GLUT_MIDDLE_BUTTON
GLUT_RIGHT_BUTTON
第二个参数表明,函数被调用发生时,鼠标的状态,也就是是被按下,或松开,可能取值如下:
GLUT_DOWN
GLUT_UP
当函数被调用时,state的值是GLUT_DOWN,那么程序可能会假定将会有个GLUT_UP事件,甚至鼠标移动到窗口外面,也如此。然而,如果程序调用glutMouseFunc传递NULL作为参数,那么GLUT将不会改变鼠标的状态。
 
剩下的两个参数(x,y)提供了鼠标当前的窗口坐标(以左上角为原点)。
 
检测动作(motion)
GLUT提供鼠标motion检测能力。有两种GLUT处理的motion:active motion和passive motion。Active motion是指鼠标移动并且有一个鼠标键被按下。Passive motion是指当鼠标移动时,并有没鼠标键按下。如果一个程序正在追踪鼠标,那么鼠标移动期间,没一帧将产生一个结果。
 
和以前一样,你必须注册将处理鼠标事件的函数(定义函数)。GLUT让我们可以指定两个不同的函数,一个追踪passive motion,另一个追踪active motion
 
它们的函数原型,如下:
void glutMotionFunc(void(*func)(int x,int y));
void glutPassiveMotionFunc(void (*func)(int x,int y));
参数:
Func:处理各自类型motion的函数名。
处理motion的参数函数的参数(x,y)是鼠标在窗口的坐标。以左上角为原点。
 
检测鼠标进入或离开窗口
GLUT还能检测鼠标鼠标离开,进入窗口区域。一个回调函数可以被定义去处理这两个事件。GLUT里,调用这个函数的是glutEntryFunc,函数原型如下:
void glutEntryFunc(void(*func)(int state));
参数:
Func:处理这些事件的函数名。
上面函数的参数中,state有两个值:
GLUT_LEFT
GLUT_ENTERED
表明,是离开,还是进入窗口。
 
把它们放一起
首先我们要做的是在GLUT里定义哪些函数将负责处理鼠标事件。因此我们将重写我们的main函数,让它包含所有必须的回调注册函数。我们将在程序里描述其他一些教程里没说清楚的地方。
void main(int argc, char **argv) {         glutInit(&argc, argv);         glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);         glutInitWindowPosition(100,100);         glutInitWindowSize(320,320);         glutCreateWindow("SnowMen");         glutDisplayFunc(renderScene);         glutIdleFunc(renderScene);         glutReshapeFunc(changeSize);          //adding here the mouse processing callbacks         glutMouseFunc(processMouse);         glutMotionFunc(processMouseActiveMotion);         glutPassiveMotionFunc(processMousePassiveMotion);         glutEntryFunc(processMouseEntry);                  glutMainLoop();}OK,现在做点有趣的。我们将定义那些将做一些不可思议事件的回调函数。当一个鼠标键和alt键都被按下,我们将改变三角形的颜色。鼠标左键使三角形变成红色,中间的将三角形变成绿色,鼠标右键将三角形变成蓝色。函数如下:
void processMouse(int button, int state, int x, int y) {           specialKey = glutGetModifiers();         // 当鼠标键和alt键都被按下         if ((state == GLUT_DOWN) &&                           (specialKey == GLUT_ACTIVE_ALT)) {                  // set the color to pure red for the left button                 if (button == GLUT_LEFT_BUTTON) {                          red = 1.0; green = 0.0; blue = 0.0;                 }                 // set the color to pure green for the middle button                 else if (button == GLUT_MIDDLE_BUTTON) {                          red = 0.0; green = 1.0; blue = 0.0;                 }                 // set the color to pure blue for the right button                 else {                          red = 0.0; green = 0.0; blue = 1.0;                 }         }}接下来有一个精细的颜色拾取方法。当一个鼠标键被按下,但alt键被被按下。我们把blue设为0.0,并且让red和green分量的值取决于鼠标在窗口中的位置。。函数如下:
void processMouseActiveMotion(int x, int y) {          // the ALT key was used in the previous function         if (specialKey != GLUT_ACTIVE_ALT) {                 // setting red to be relative to the mouse                  // position inside the window                 if (x < 0)                          red = 0.0;                 else if (x > width)                          red = 1.0;                 else                          red = ((float) x)/height;                 // setting green to be relative to the mouse                  // position inside the window                 if (y < 0)                          green = 0.0;                 else if (y > width)                          green = 1.0;                 else                          green = ((float) y)/height;                 // removing the blue component.                 blue = 0.0;         }}下面给passive motion添加一些动作。当shift键被按下,鼠标将在x轴上有一个旋转。我们不得不修改renderScene函数。函数如下:
float angleX = 0.0;...void renderScene(void) {         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);         glPushMatrix();         glRotatef(angle,0.0,1.0,0.0);                  // This is the line we added for the         // rotation on the X axis;         glRotatef(angleX,1.0,0.0,0.0);                  glColor3f(red,green,blue);          glBegin(GL_TRIANGLES);                 glVertex3f(-0.5,-0.5,0.0);                 glVertex3f(0.5,0.0,0.0);                 glVertex3f(0.0,0.5,0.0);         glEnd();         glPopMatrix();         angle++;         glutSwapBuffers();}现在我们的有个函数处理passive motion事件。函数将改变angleX的值。void processMousePassiveMotion(int x, int y) {          // User must press the SHIFT key to change the          // rotation in the X axis         if (specialKey != GLUT_ACTIVE_SHIFT) {                  // setting the angle to be relative to the mouse                  // position inside the window                 if (x < 0)                          angleX = 0.0;                 else if (x > width)                          angleX = 180.0;                 else                          angleX = 180.0 * ((float) x)/height;         }}最后鼠标离开窗口将使动画停止,为了做到这,我们也需要改变函数renderScene。// initially define the increase of the angle by 1.0;float deltaAngle = 1.0;...void renderScene(void) {         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);         glPushMatrix();         glRotatef(angle,0.0,1.0,0.0);         glRotatef(angleX,1.0,0.0,0.0);         glColor3f(red,green,blue);          glBegin(GL_TRIANGLES);                 glVertex3f(-0.5,-0.5,0.0);                 glVertex3f(0.5,0.0,0.0);                 glVertex3f(0.0,0.5,0.0);         glEnd();         glPopMatrix();         // this is the new line         // previously it was: angle++;         angle+=deltaAngle;         glutSwapBuffers();}processMouseEntry是最后一个函数。注意,这个在微软操作系统下可能工作的不是很好。void processMouseEntry(int state) {         if (state == GLUT_LEFT)                 deltaAngle = 0.0;         else                 deltaAngle = 1.0;}VC6.0工程可以在这里下载(glut8.zip)。 (到这里位置,键盘,鼠标方面的控制讲完了,下面就是菜单了。)(原文地址:http://www.lighthouse3d.com/opengl/glut/index.php?9

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/xie_zi/archive/2007/12/09/1925778.aspx

posted @ 2010-11-08 12:26 未央 阅读(9101) | 评论 (1)编辑 收藏
 
【转】http://dacuisworld.spaces.live.com/blog/cns!5194FC8976D233A0!1121.entry
重装电脑之后按照以上方法可顺利配置OpenGL编程环境!重装了真好!
posted @ 2010-11-02 14:52 未央 阅读(297) | 评论 (0)编辑 收藏
 

在VISUAL C++ 2008上配置OPENGL开发环境

这篇文章写的太有用了!我想配VC2008的OpenGL,添加了5个文件后仍然找不到glaux.h,很是着急啊,直到这篇文章解救了我。  !!!非常感谢!

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
应该在
VISUAL C++ 2005/VC 6.0/VC7.0同样都适用

首先感谢网上写VC6.0配置OPENGL开发环境的作者,我是先在你那里学习了如何配置,只是做了一点小的延伸,主要内容还是你的。

第一步:下载OpenGLGLUT

Windows环境下的GLUT下载地址:(苹果机不需要安装,自带)  

http://www.opengl.org/resources/libraries/glut/glutdlls37beta.zip

第二步:OpenGL库和配置文件

OpenGL库配置用到的文件分为下面三类:

■ 动态链接库文件(.dll

glaux.dll, glu32.dll, glut32.dll, OPENGL32.DLL, glut.dll

■ 头文件(.h

GL.H, GLAUX.H, GLU.H, glut.h

■ 库文件(.lib

GLAUX.LIBGlu32.libglut32.libOpengl32.libglut.lib

 

其中opengl32.dll, glaux.dll,glu32.dll是安装显卡驱动自带,应该每个系统里面都有,如果没有重新安装显卡驱动。

其中glut32.dll, glut.dll, glut.h, glut32.lib, glut.lib 是在刚才那个地址下载的,打开压缩包后会有5个文件

下面就是区别了,VC++ 2008不带GL.H, GLAUX.h, glu.h, glaux.lib, glu32.lib, opengl32.lib这些文件要在网上下载或者在VC6.0里面拷贝出来,

如果想要全套的文件,给我发邮件我给你发xudongcui@gmail.com

 

第三步:Windows下配置OpenGL

glut32.dll, glut.dll拷贝到C:\WINDOWS\system32目录下,system32目录下应该已经有 opengl32.dll, glu32.dll了。

GL.H, GLAUX.h, glu.h, glut.h  拷贝到 C:\Program Files\Microsoft Visual Studio 9.0\VC\include\gl

GLAUX.LIBGlu32.libglut32.libOpengl32.libglut.lib拷贝到 C:\Program Files\Microsoft Visual Studio 9.0\VC\lib

第四步:建立VC++2008工程

打开VC++2008,选择新建工程,Win32Win32控制台应用程序。删除默认的所有代码,用如下代码替换。

#include "stdafx.h"

#include <windows.h>

#include <GL/glu.h>

#include <GL/gl.h>

#include <GL/glut.h>

#include <GL/glaux.h>

 

void background(void)

{

//设置背景颜色为黑色

glClearColor(0.0,0.0,0.0,0.0);

}

 

void myDisplay(void)

{

//buffer设置为颜色可写

glClear(GL_COLOR_BUFFER_BIT);

//开始画三角形

glBegin(GL_TRIANGLES);

//设置为光滑明暗模式

glShadeModel(GL_SMOOTH);

//设置第一个顶点为红色

glColor3f(1.0,0.0,0.0);

//设置第一个顶点的坐标为(-1.0-1.0

glVertex2f(-1.0,-1.0);

//设置第二个顶点为绿色

glColor3f(0.0,1.0,0.0);

//设置第二个顶点的坐标为(0.0-1.0

glVertex2f(0.0,-1.0);

//设置第三个顶点为蓝色

glColor3f(0.0,0.0,1.0);

//设置第三个顶点的坐标为(-0.51.0

glVertex2f(-0.5,1.0);

//三角形结束

glEnd();

//强制OpenGL函数在有限时间内运行

glFlush();

}

 

void myReshape(GLsizei w,GLsizei h)

{

glViewport(0,0,w,h);

//设置视口

 

glMatrixMode(GL_PROJECTION);

//指明当前矩阵为GL_PROJECTION

glLoadIdentity();

//将当前矩阵置换为单位阵

 

if(w <= h)

gluOrtho2D(-1.0,1.5,-1.5,1.5*(GLfloat)h/(GLfloat)w);

//定义二维正视投影矩阵

else

gluOrtho2D(-1.0,1.5*(GLfloat)w/(GLfloat)h,-1.5,1.5);

glMatrixMode(GL_MODELVIEW);

//指明当前矩阵为GL_MODELVIEW

}

int main(int argc, char* argv[])

{

// 初始化

glutInit(&argc,argv);

glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);

glutInitWindowSize(400,400);

glutInitWindowPosition(200,200);

 

//创建窗口

glutCreateWindow("Triangle");

 

//绘制与显示

background();

glutReshapeFunc(myReshape);

glutDisplayFunc(myDisplay);

 

glutMainLoop();

return(0);

}

posted @ 2010-09-30 13:11 未央 阅读(7480) | 评论 (1)编辑 收藏
 
pku 1050 最大子矩阵和

题目大意:
给定一个N*N的矩阵,求其中一个子矩阵所有元素的和最大,输出最大值。

题解:
    这道题很早就见过了,一直不会做,学了最大连续和,但是没能成功迁移,看别人的解题报告也是很久才理解。
主要思想就是把二维的矩阵转化成一位的数字串,然后求最大子串和。转换的时候,为了保证最大子串构成的是完整的矩形,所以串里的每一个元素都得是一列的和。枚举子矩阵的起始行和高度,如从第i行开始,到第j行结束,每一对 i 和 j,对每一列(1~n)求和,然后求1~n串的最大子串和。
#include<stdio.h>
#include
<string.h>
const int N = 110;
int g[N][N], f[N];
int main(){
    
int n;
    
while(scanf("%d"&n)!=EOF){
        memset(f, 
0sizeof(f));
        
for(int i=1; i<=n; i++)
            
for(int j=1; j<=n; j++)
                scanf(
"%d"&g[i][j]);
        
int mx=-100000000;
        
for(int i=1; i<=n; i++)
            
for(int j=i ; j<=n; j++){
                memset(f, 
0sizeof(f));
                
for(int s=1; s<=n; s++)
                    
for(int k=i; k<=j; k++)
                        f[s]
+=g[k][s];
                
int tmp=0;
                
for(int s=1; s<=n; s++){
                    
if(tmp>0)
                        tmp
+=f[s];
                    
else
                        tmp
=f[s];
                    mx
=mx>tmp?mx:tmp;
                }

            }

        printf(
"%d\n",mx);

    }

    
return 0;
}



posted @ 2010-09-03 22:58 未央 阅读(209) | 评论 (0)编辑 收藏
 
         最近被逼的不行了,所以动真格的学动规!先是把《程序设计导引及在线实践》这本书里动规的例题都看过、做过,本来想一道一道的做习题的,结果poj挂了。于是转战hdu,昨天做了一道三维树状数组,比较理解了,今天又继续hdu的动规,感觉做过的题型,换个背景再做能够想到并且实现了,细节上还需注意。
hdu 1160 FatMouse's Speed
题目大意:
给定n只老鼠的体重w和速度s,求一个最长的排列,使得这个排列中的老鼠体重严格递增,速度严格递减。题目看上去很像最长上升子序列,只是这里的序列是可以打乱顺序按体重排序的。

解题思路:
整体思路同最长上升子序列。
1、按老鼠的体重递增排序,当老鼠的体重相等时按速度递减排序。
2、f[j]表示前j只老鼠中最长序列的长度,状态转移方程为
      f[N]初始值为0;
      f[j]=max{ f[i] : 0<=i<j 且 s[j]<s[i] }+1;
      也就是说,如果第j只老鼠是最长序列中的话,那么它肯定比上一个在最长序列中老鼠体重更大 ( j>i ) 且速度更小 ( s[j] < s[i] )。

代码如下:
#include<stdio.h>
#include
<string.h>
#include
<algorithm>
#include
<stdlib.h>
using namespace std;
const int N = 1010;
struct Mouse{
    
int w, s, num;
}
m[N];
int f[N], pre[N];
bool cmp(Mouse a, Mouse b){
    
if(a.w<b.w)
        
return true;
    
else if (a.w==b.w)
        
return a.s>b.s;
    
return false;
}

int main(){
    
int i=0,n=0;
    
while(scanf("%d %d"&m[n].w, &m[n].s)!=EOF){
        m[n].num
=n+1;
        n
++;
    }

    sort(m, m
+n, cmp);
    memset(f, 
0sizeof(f));
    memset(pre, 
-1sizeof(pre));
    
int mx=0,end=0;
    
for(i=1; i<n; i++){
        
for(int j=0; j<i; j++){
            
if(m[i].s<m[j].s && m[i].w>m[j].w){
                
if(f[j]+1>f[i]){
                    f[i]
=f[j]+1;
                    pre[i]
=j;
                }

            }

        }

        
if(f[i]>mx){
            mx
=f[i];
            end
=i;
        }

    }

    printf(
"%d\n", mx+1);
    
int ans[N], len=0, x;
    x
=end; ans[0]=x;
    
while(pre[x]>=0){
        ans[
++len]=pre[x];
        x
=pre[x];
        
if(x<0break;
    }

    
for(i=len; i>=0; i--)
        printf(
"%d\n", m[ans[i]].num);
    
return 0;
}

posted @ 2010-09-01 17:39 未央 阅读(180) | 评论 (0)编辑 收藏
 
Java课留作业,要求实现“在Frame窗口中设计一个文本框和一个文本区域,文本框内容改变时,将文本框中的内容显示在文本区域中”,这个问题可害苦了我,查了好多方法都不行,还是对Java里面的类不了解啊。
解决办法:
创建文本框时不要用JTextField, 因为这个类里面没有addTextListener的方法,所以声明文本框的时候应该这样写:
    java.awt.TextField textField=new java.awt.TextField();  
然后再给 textField 加一个监听器
    textField.addTextListener(new TextListener(){
            public void textValueChanged(TextEvent e){
                ta.setText(textField.getText());
            }
        });
就可以实时在文本域里显示文本框的改变了。
⊙﹏⊙b汗,这个问题纠结了好久
感谢koy师兄的鼎力支持和耐心讲解,好人啊!

posted @ 2010-05-23 17:27 未央 阅读(3403) | 评论 (1)编辑 收藏
 
        现在才做这个决定其实已经有些晚了,但是人就是这样,可以早作打算的时候往往不知道该怎么打算,好在我习惯了很多事情都在最后时刻奋力争取。过年这些天很多人都对我妈说,该让孩子出去见识见识,别拦着。妈妈也想开了,有了她的支持比任何利益诱惑都更能坚定我。于是我想给自己找一个不得不出国的理由,然后坚定信念去拼最后半年,但是我发现这个理由好难找,后来我知道了,很多事情之所以这样做并不一定是出于必然,而是个人的喜好和性格,这样的话,选择就无所谓对错,只有喜欢与否。就像选专业一样,选到喜欢的就是对的。好吧,那就让自己离开家吧,像去杭州和哈尔滨一样,下了火车没感觉有多远,照样是人们生活的地方。
        我的学分也渐渐上来了,竞赛也有了一些成果,接下来该做的事情就是考英语的G、T和完成实验室Vuze的项目,最好能发一篇论文,保持成绩和竞赛。时间紧,任务重,没有心思想别的了,努力吧:)
       新年新愿望:明年的这个时候希望有好的offer圆了我的留学梦^-^
posted @ 2010-02-25 10:28 未央 阅读(330) | 评论 (1)编辑 收藏
 
包含点集所有点的最小圆的算法最小圆覆盖 http://acm.zju.edu.cn/show_problem.php?pid=1450 相关题目最小球包含 http://acm.pku.edu.cn/JudgeOnline/problem?id=2069 平面上有n个点,给定n个点的坐标,试找一个半径最小的圆,将n 个点全部包围,点可以在圆上。 1. 在点集中任取3点A,B,C。 2. 作一个包含A,B,C三点的最小圆,圆周可能通过这3点,也可能只通过其中两点,但包含第3点.后一种情况圆周上的两点一定是位于圆的一条直径的两端。 3. 在点集中找出距离第2步所建圆圆心最远的D点,若D点已在圆内或圆周上,则该圆即为所求的圆,算法结束.则,执行第4步。 4. 在A,B,C,D中选3个点,使由它们生成的一个包含这4个点的圆为最小,这3 点成为新的A,B,C,返回执行第2步。若在第4步生成的圆的圆周只通过A,B,C,D 中的两点,则圆周上的两点取成新的A和B,从另两点中任取一点作为新的C。 程序设计题解上的解题报告:对于一个给定的点集A,记MinCircle(A)为点集A的最小外接圆,显然,对于所有的点集情况A,MinCircle(A)都是存在且惟一的。需要特别说明的是,当A为空集时,MinCircle(A)为空集,当A={a}时,MinCircle(A)圆心坐标为a,半径为0; 显然,MinCircle(A)可以有A边界上最多三个点确定(当点集A中点的个数大于 1时,有可能两个点确定了MinCircle(A)),也就是说存在着一个点集B,|B|<=3 且B包含与A,有MinCircle(B)=MinCircle(A).所以,如果a不属于B,则 MinCircle(A-{a})=MinCircle(A);如果MinCircle(A-{a})不等于MinCircle(A),则 a属于B。 所以我们可以从一个空集R开始,不断的把题目中给定的点集中的点加入R,同时维护R的外接圆最小,这样就可以得到解决该题的算法。
zju 1450
题目描述:给定n个点,求覆盖所有点的圆的圆心和半径
#include<stdio.h>
#include
<math.h>
#include
<string.h>
#define MAXN 20
struct pointset{
    
double x, y;
};
const double precison=1.0e-8;
pointset maxcic, point[MAXN];
double radius;
int curset[MAXN], posset[3];
int set_cnt, pos_cnt;
inline 
double dis_2(pointset &from, pointset& to){
    
return ((from.x-to.x)*(from.x-to.x)+(from.y-to.y)*(from.y-to.y));
}
int in_cic(int pt){
    
if(sqrt(dis_2(maxcic, point[pt]))<radius+precison) return 1;
    
return 0;
}
int cal_mincic(){
    
if(pos_cnt==1 || pos_cnt==0)
        
return 0;
    
else if(pos_cnt==3){
        
double A1, B1, C1, A2, B2, C2;
        
int t0=posset[0], t1=posset[1], t2=posset[2];
        A1
=2*(point[t1].x-point[t0].x);
        B1
=2*(point[t1].y-point[t0].y);
        C1
=point[t1].x*point[t1].x-point[t0].x*point[t0].x+
            point[t1].y
*point[t1].y-point[t0].y*point[t0].y;
        A2
=2*(point[t2].x-point[t0].x);
        B2
=2*(point[t2].y-point[t0].y);
        C2
=point[t2].x*point[t2].x-point[t0].x*point[t0].x+
            point[t2].y
*point[t2].y-point[t0].y*point[t0].y;
        maxcic.y
=(C1*A2-C2*A1)/(A2*B1-A1*B2);
        maxcic.x
=(C1*B2-C2*B1)/(A1*B2-A2*B1);
        radius
=sqrt(dis_2(maxcic, point[t0]));
    }
    
else if(pos_cnt==2){
        maxcic.x
=(point[posset[0]].x+point[posset[1]].x)/2;
        maxcic.y
=(point[posset[0]].y+point[posset[1]].y)/2;
        radius
=sqrt(dis_2(point[posset[0]], point[posset[1]]))/2;
    }
    
return 1;
}
int mindisk(){
    
if(set_cnt==0 || pos_cnt==3){
        
return cal_mincic();
    }
    
int tt=curset[--set_cnt];
    
int res=mindisk();
    set_cnt
++;
    
if(!res || !in_cic(tt)){
        set_cnt
--;
        posset[pos_cnt
++]=curset[set_cnt];
        res
=mindisk();
        pos_cnt
--;
        curset[set_cnt
++]=curset[0];
        curset[
0]=tt;
    }
    
return res;
}
int main(){
    freopen(
"in2.txt""r", stdin);
    freopen(
"radius.txt""w", stdout);
    
int n;
    
while(scanf("%d"&n)!=EOF){
        
if(n==0break;
        
int i;
        
for(i=0; i<n; i++)
            scanf(
"%lf %lf"&point[i].x, &point[i].y);
            
if(n==1){
                maxcic.x
=point[0].x;
                maxcic.y
=point[0].y;
                radius
=0;
                printf(
"%.2lf %.2lf %.2lf\n", maxcic.x, maxcic.y, radius);
                
continue;
            }
            set_cnt
=n; pos_cnt=0;
            
for(i=0 ;i<n ;i++)  curset[i]=i;
            mindisk();
            printf(
"%.2lf %.2lf %.2lf\n", maxcic.x, maxcic.y, radius);
    }

    
return 0;
}
posted @ 2010-02-21 12:03 未央 阅读(4157) | 评论 (5)编辑 收藏
 

一个标准的Joseph问题,n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。
我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m%n的人开始):
   k   k+1   k+2   ... n-2, n-1, 0, 1, 2, ... k-2
并且从k开始报0。

现在我们把他们的编号做一下转换:
k      --> 0
k+1    --> 1
k+2    --> 2
...
...
k-2    --> n-2
k-1    --> n-1

变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗?!!变回去的公式很简单,相信大家都可以推出来:x'=(x+k)%n

如何知道(n-1)个人报数的问题的解?对,只要知道(n-2)个人的解就行了。(n-2)个人的解呢?当然是先求(n-3)的情况 ---- 这显然就是一个倒推问题!好了,思路出来了,下面写递推公式:

令f表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n]

递推公式
f[1]=0;
f[i]=(f[i-1]+m)%i;   (i>1)


有了这个公式,我们要做的就是从1-n顺序算出f的数值,最后结果是f[n]。因为实际生活中编号总是从1开始,我们输出f[n]+1

由于是逐级递推,不需要保存每个f,程序也比较简单:

 

#include <stdio.h>
int main ()
{
int n,m,i,s;
while (scanf("%d %d",&n,&m)!=EOF)
{
   
if (n==0 && m==0return 0;
   s
=0;
   
for (i=2;i<=n;i++)
    s
=(s+m)%i;
   printf (
"%d %d %d\n",n,m,s+1);
}
return 0;
}



 

posted @ 2009-10-10 19:11 未央 阅读(871) | 评论 (0)编辑 收藏
仅列出标题
共8页: 1 2 3 4 5 6 7 8 
CALENDER
<2009年8月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
303112345

常用链接

留言簿(6)

随笔档案

文章档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜


Powered By: 博客园
模板提供沪江博客