近段时间做一个关于水面的动画。由于我用c++实现水面动画的,然而使用c++我自己的渲染系统渲染结果被同学说是可视化不叫渲染,所以我决定修改一下……

恰好进来在学习pbrt,所以索性就蛋疼了考虑直接用pbrt来渲染吧……(至于为什么,仅为好玩儿……)

pbrt默认的渲染方式是使用一个场景描述文件.pbrt,我要渲染的对象是三角网络就必须使用对应的描述语句定义三角对象:

Shape "trianglemesh"  

"integer indices" [   ] 

"point P" [  0.401925   0.92604   0.950944 ] 

"normal N" [-0.245007 -0.256649 0.934935 -0.439362 -0.242124 0.865065 -0.228464 -0.466945 0.854264 ]

对应的参数都很清晰不多说了,我的方法就是将我的三角网络拆成三角形按照这个格式一个一个输出到文件,mesh.pbrt:

Shape "trianglemesh"  "integer indices" [   ] "point P" [  0.401925   0.92604   0.950944 ] "normal N" [-0.245007 -0.256649 0.934935 -0.439362 -0.242124 0.865065 -0.228464 -0.466945 0.854264 ]
Shape "trianglemesh" "integer indices" [ ] "point P" [ 0.92604 1.48582 0.950944 ] "normal N" [-0.439362 -0.242124 0.865065 -0.387612 -0.436964 0.811677 -0.228464 -0.466945 0.854264 ]
Shape "trianglemesh" "integer indices" [ ] "point P" [ 0.92604 1.41771 1.48582 ] "normal N" [-0.439362 -0.242124 0.865065 -0.371551 -0.220212 0.901918 -0.387612 -0.436964 0.811677 ]
......

按照pbrt给出的例子写出场景主文件:

#定义表面积分器
SurfaceIntegrator "directlighting"
#transform
ConcatTransform [ 0.828849 -0.295370 -0.475149 0.000000 -0.559473 -0.437585 -0.703924 0.000000 -0.000000 0.849280 -0.527943 0.000000 0.000000 0.000000 0.000000 1.000000 ]
Translate -4.860000 -7.200000 -5.400000
#定义摄像机
Camera "perspective" "float fov" [90.00000 ] "float shutteropen" [0.000000 ] "float shutterclose" [0.000000 ] "float screenwindow" [-1.000000 1.000000 -1.000000 1.000000 ] "float frameaspectratio" [1.333333 ]
#定义胶片
Film "image" "integer xresolution" [ ] "integer yresolution" [ ]
"string filename" "1.exr"
#定义采样器
Sampler "lowdiscrepancy" "integer pixelsamples" []
PixelFilter "box" WorldBegin
AttributeBegin
LightSource "infinite" "string mapname" ["textures/skylight-dusk.exr"]
"color L" [0.5 0.5 0.5]
"integer nsamples" []
AttributeEnd AttributeBegin
#mesh的材质
Material "uber" "color Kd" [ 0.1 0.15] "color Kr" [0.9 0.9 0.9] "color Ks" [0.1 0.1 0.1] "float roughness" [0.9] "float index" [1.34]
#mesh trnsform
Translate - - -
Scale 0.1 0.1 0.3
#包含mesh
Include "mesh.pbrt"
AttributeEnd WorldEnd

(更多的pbrt场景命令你可以去官网下文档来看)

执行下列命令(当然你要先编译好pbrt,这个直接下pbrt-v2编译就好,没什么需要说明的地方):

pbrt scene.pbrt

执行pbrt就会出现一个进度条开始渲染对象,渲染完成后就会在当前目录输出一个1.exr图像。

但是我要做的是动画,每帧mesh有至少有N*N*2个三角面,N>=512,我的动画打算30秒,20帧,一共600张图,需要600个mesh,这个要是手动来一个一个搞,别说mesh输出一共要600*100M+,就是人也要累死啊。

考虑用写个批处理来搞一下感觉也不好使,后来反正因为学习pbrt就决定把pbrt的代码嵌入到c++程序中去算了。

pbrt代码本身唯一的文档,就是pbrt那本书了,但是型号它的API很清晰易懂,几乎每个api都是场景描述文件对应的,所以我只需要把场景pbrt文件翻译成c++代码就可以调用pbrt了,pbrt的API如下:

void pbrtInit(const Options &opt);
void pbrtCleanup();
void pbrtIdentity();
void pbrtTranslate(float dx, float dy, float dz);
void pbrtRotate(float angle, float ax, float ay, float az);
void pbrtScale(float sx, float sy, float sz);
void pbrtLookAt(float ex, float ey, float ez,float lx, float ly, float lz,float ux, float uy, float uz);
void pbrtConcatTransform(float transform[]);
void pbrtTransform(float transform[]);
void pbrtCoordinateSystem(const string &);
void pbrtCoordSysTransform(const string &);
void pbrtActiveTransformAll();
void pbrtActiveTransformEndTime();
void pbrtActiveTransformStartTime();
void pbrtTransformTimes(float start, float end);
void pbrtPixelFilter(const string &name, const ParamSet &params);
void pbrtFilm(const string &type, const ParamSet &params);
void pbrtSampler(const string &name, const ParamSet &params);
void pbrtAccelerator(const string &name, const ParamSet &params);
void pbrtSurfaceIntegrator(const string &name, const ParamSet &params);
void pbrtVolumeIntegrator(const string &name, const ParamSet &params);
void pbrtRenderer(const string &name, const ParamSet &params);
void pbrtCamera(const string &, const ParamSet &cameraParams);
void pbrtWorldBegin();
void pbrtAttributeBegin();
void pbrtAttributeEnd();
void pbrtTransformBegin();
void pbrtTransformEnd();
void pbrtTexture(const string &name, const string &type,const string &texname, const ParamSet &params);
void pbrtMaterial(const string &name, const ParamSet &params);
void pbrtMakeNamedMaterial(const string &name, const ParamSet &params);
void pbrtNamedMaterial(const string &name);
void pbrtLightSource(const string &name, const ParamSet &params);
void pbrtAreaLightSource(const string &name, const ParamSet &params);
void pbrtShape(const string &name, const ParamSet &params);
void pbrtReverseOrientation();
void pbrtVolume(const string &name, const ParamSet &params);
void pbrtObjectBegin(const string &name);
void pbrtObjectEnd();
void pbrtObjectInstance(const string &name);
void pbrtWorldEnd();

对比一下场景描述文件,发现基本一一对应。

pbrt本身是被编译成lib静态库的,而pbrt.exe仅仅是一个引用pbrtlib的单文件c++程序而已:

#include "stdafx.h"
#include "api.h"
#include "probes.h"
#include "parser.h"
#include "parallel.h" // main program
int main(int argc, char *argv[]) {
Options options;
vector<string> filenames;
// Process command-line arguments
for (int i = ; i < argc; ++i) {
if (!strcmp(argv[i], "--ncores")) options.nCores = atoi(argv[++i]);
else if (!strcmp(argv[i], "--outfile")) options.imageFile = argv[++i];
else if (!strcmp(argv[i], "--quick")) options.quickRender = true;
else if (!strcmp(argv[i], "--quiet")) options.quiet = true;
else if (!strcmp(argv[i], "--verbose")) options.verbose = true;
else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) {
printf("usage: pbrt [--ncores n] [--outfile filename] [--quick] [--quiet] "
"[--verbose] [--help] <filename.pbrt> ...\n");
return ;
}
else filenames.push_back(argv[i]);
} // Print welcome banner
if (!options.quiet) {
printf("pbrt version %s of %s at %s [Detected %d core(s)]\n",
PBRT_VERSION, __DATE__, __TIME__, NumSystemCores());
printf("Copyright (c)1998-2014 Matt Pharr and Greg Humphreys.\n");
printf("The source code to pbrt (but *not* the book contents) is covered by the BSD License.\n");
printf("See the file LICENSE.txt for the conditions of the license.\n");
fflush(stdout);
}
pbrtInit(options);
// Process scene description
PBRT_STARTED_PARSING();
if (filenames.size() == ) {
// Parse scene from standard input
ParseFile("-");
} else {
// Parse scene from input files
for (u_int i = ; i < filenames.size(); i++)
if (!ParseFile(filenames[i]))
Error("Couldn't open scene file \"%s\"", filenames[i].c_str());
}
pbrtCleanup();
return ;
}

其中起到解析文件作用就是如下这段:

    pbrtInit(options);
// Process scene description
PBRT_STARTED_PARSING();
if (filenames.size() == ) {
// Parse scene from standard input
ParseFile("-");
} else {
// Parse scene from input files
for (u_int i = ; i < filenames.size(); i++)
if (!ParseFile(filenames[i]))
Error("Couldn't open scene file \"%s\"", filenames[i].c_str());
}

它是使用yacc解析的,细节我并不懂,我把这个文件换成如下:

#include "stdafx.h"
#include "api.h"
#include "probes.h"
#include "parser.h"
#include "parallel.h"
#include "paramset.h" extern void InitParamSet(ParamSet &ps, SpectrumType); void create_a_tri(
Point i1,Point i2,Point i3,
Normal n1,Normal n2,Normal n3
)
{
ParamSet params;
//mesh参数
int s[] = {,,};
params.AddInt("indices",s,);
Point p[] = {i1,i2,i3};
params.AddPoint("P",p,);
Normal n[] = {n1,n2,n3};
params.AddNormal("N",n,);
pbrtShape("trianglemesh",params);
} int main(int argc, char *argv[])
{
Options opt;
opt.imageFile = "1.exr";
opt.nCores = ;
opt.openWindow = false;
opt.quickRender = false;
pbrtInit(opt); ParamSet params;
/*
InitParamSet(params, SPECTRUM_REFLECTANCE);
pbrtSurfaceIntegrator("directlighting",params);
*/ //初始化所有参数
//表面积分器参数
int maxdepth = ;
params.AddInt("maxdepth",&maxdepth,);
string strategy = "all";
params.AddString("strategy",&strategy,); //相机参数
float fov = ;
params.AddFloat("fov",&fov,);
float shutteropen = 0.00f;
params.AddFloat("shutteropen",&shutteropen,);
float shutterclose = 0.00f;
params.AddFloat("shutterclose",&shutterclose,);
float screenwindow[] = {-1.0,1.0,-1.0,1.0};
params.AddFloat("screenwindow",screenwindow,);
float ratio = 1.333333;
params.AddFloat("frameaspectratio",&ratio,);
//胶片
int xresolution = ;
int yresolution = ;
string filename = "1.exr";
params.AddInt("xresolution",&xresolution,);
params.AddInt("yresolution",&yresolution,);
params.AddString("filename",&filename,);
//采样器
int pixelsample = ;
params.AddInt("pixelsamples",&pixelsample,);
//过滤器
float xwidth = 0.5;
float ywidth = 0.5;
params.AddFloat("xwith",&xwidth,);
params.AddFloat("ywith",&ywidth,);
//光照
float L[] = {0.5,0.5,0.5};
int nsamples = ;
string mapname = "textures/skylight-dusk.exr";
params.AddRGBSpectrum("L",L,);
params.AddInt("nsamples",&nsamples,);
params.AddString("mapname",&mapname,);
//材质
float Kd[] = {,0.1,0.15};
float Kr[] = {0.9,0.9,0.9};
float Ks[] = {0.1,0.1,0.1};
float roughness = 0.9;
float index = 1.34;
params.AddRGBSpectrum("Kd",Kd,);
params.AddRGBSpectrum("Kr",Kr,);
params.AddRGBSpectrum("Ks",Ks,);
params.AddFloat("roughness",&roughness,);
params.AddFloat("index",&index,); pbrtSurfaceIntegrator("directlighting",params);
float ct[] = {0.828849,-0.295370,-0.475149,0.000000,-0.559473,-0.437585,-0.703924,0.000000,-0.000000,0.849280,-0.527943,0.000000,0.000000,0.000000,0.000000,1.000000};
pbrtConcatTransform(ct);
pbrtTranslate(-4.860000,-7.200000,-5.400000);
pbrtCamera("perspective",params);
pbrtFilm("image",params);
pbrtSampler("lowdiscrepancy",params);
pbrtPixelFilter("box",params); pbrtWorldBegin();
pbrtAttributeBegin();
pbrtLightSource("infinite",params);
pbrtAttributeEnd(); pbrtAttributeBegin();
pbrtMaterial("uber",params);
//pbrtTranslate(-90,-80,-2);
//pbrtScale(0.1,0.1,0.4);
pbrtTranslate(-,-,-);
pbrtScale(,,);
//mesh参数
int s[] = {,,};
params.AddInt("indices",s,);
Point p[] = {Point(,,11.8604),Point(,,11.2012),Point(,,11.2039)};
params.AddPoint("P",p,);
Normal n[] = {Normal(13.1851,13.1312,),Normal(25.1371,13.4084,),Normal(13.4624,25.8694,)};
params.AddNormal("N",n,);
pbrtShape("trianglemesh",params); create_a_tri(Point(,,11.8604),Point(,,11.2012),Point(,,11.2039),Normal(13.1851,13.1312,),Normal(25.1371,13.4084,),Normal(13.4624,25.8694,));
create_a_tri(Point(,,11.2012),Point(,,10.5308),Point(,,11.2039),Normal(25.1371,13.4084,),Normal(21.2989,23.2563,),Normal(13.4624,25.8694,));
pbrtAttributeEnd(); pbrtWorldEnd(); pbrtCleanup();
return ;
}

很好,和pbrt文件渲染出来类似结果。

有了这些就可以把它嵌入到of中了,定义两个函数:

void create_a_tri(
Point i1,Point i2,Point i3,
Normal n1,Normal n2,Normal n3
); int render(const char* file_name,FloatPixels** pxs,int N,float LL,float lamda,float nla);

第一个创建一个三角形,第二个就是使用c++创建pbrt场景渲染输出。实现一下:

#include "render.h"
#include "core\stdafx.h"
#include "core\api.h"
#include "core\probes.h"
#include "core\parser.h"
#include "core\parallel.h"
#include "core\paramset.h" void create_a_tri(
Point i1,Point i2,Point i3,
Normal n1,Normal n2,Normal n3
)
{
ParamSet params;
//mesh参数
int s[] = {,,};
params.AddInt("indices",s,);
Point p[] = {i1,i2,i3};
params.AddPoint("P",p,);
Normal n[] = {n1,n2,n3};
params.AddNormal("N",n,);
pbrtShape("trianglemesh",params);
} int render(const char* file_name,FloatPixels** pxs,int N,float LL,float lamda,float nla)
{
printf("Generating Mesh...\n");
Options opt;
opt.nCores = ;
opt.openWindow = false;
opt.quickRender = false;
pbrtInit(opt); ParamSet params; //初始化所有参数
//表面积分器参数
int maxdepth = ;
params.AddInt("maxdepth",&maxdepth,);
string strategy = "all";
params.AddString("strategy",&strategy,); //相机参数
float fov = ;
params.AddFloat("fov",&fov,);
float shutteropen = 0.00f;
params.AddFloat("shutteropen",&shutteropen,);
float shutterclose = 0.00f;
params.AddFloat("shutterclose",&shutterclose,);
float screenwindow[] = {-1.0,1.0,-1.0,1.0};
params.AddFloat("screenwindow",screenwindow,);
float ratio = 1.333333;
params.AddFloat("frameaspectratio",&ratio,);
//胶片
int xresolution = ;
int yresolution = ;
string filename = file_name;
params.AddInt("xresolution",&xresolution,);
params.AddInt("yresolution",&yresolution,);
params.AddString("filename",&filename,);
//采样器
int pixelsample = ;
params.AddInt("pixelsamples",&pixelsample,);
//过滤器
float xwidth = 0.5;
float ywidth = 0.5;
params.AddFloat("xwith",&xwidth,);
params.AddFloat("ywith",&ywidth,);
//光照
float L[] = {0.5,0.5,0.5};
int nsamples = ;
string mapname = "textures/skylight-dusk.exr";
params.AddRGBSpectrum("L",L,);
params.AddInt("nsamples",&nsamples,);
params.AddString("mapname",&mapname,);
//材质
float Kd[] = {,0.1,0.15};
float Kr[] = {0.9,0.9,0.9};
float Ks[] = {0.1,0.1,0.1};
float roughness = 0.9;
float index = 1.34;
params.AddRGBSpectrum("Kd",Kd,);
params.AddRGBSpectrum("Kr",Kr,);
params.AddRGBSpectrum("Ks",Ks,);
params.AddFloat("roughness",&roughness,);
params.AddFloat("index",&index,); pbrtSurfaceIntegrator("directlighting",params);
float ct[] = {0.828849,-0.295370,-0.475149,0.000000,-0.559473,-0.437585,-0.703924,0.000000,-0.000000,0.849280,-0.527943,0.000000,0.000000,0.000000,0.000000,1.000000};
pbrtConcatTransform(ct);
pbrtTranslate(-4.860000,-7.200000,-5.400000);
pbrtCamera("perspective",params);
pbrtFilm("image",params);
pbrtSampler("lowdiscrepancy",params);
pbrtPixelFilter("box",params); pbrtWorldBegin();
pbrtAttributeBegin();
pbrtLightSource("infinite",params);
pbrtAttributeEnd(); pbrtAttributeBegin();
pbrtMaterial("uber",params);
pbrtTranslate(-,-,-);
pbrtScale(0.1,0.1,0.1);
for(int i=;i<N-;i++)
{
for(int j=;j<N-;j++)
{ create_a_tri(
Point((j+lamda*pxs[]->getColor(j,i).r)*LL/N,(i+lamda* pxs[]->getColor(j,i).r)*LL/N,pxs[]->getColor(j,i).r*N/),
Point((j++lamda*pxs[]->getColor(j+,i).r)*LL/N,(i+lamda* pxs[]->getColor(j+,i).r)*LL/N,pxs[]->getColor(j+,i).r*N/),
Point((j+lamda*pxs[]->getColor(j,i+).r)*LL/N,(i++lamda* pxs[]->getColor(j,i+).r)*LL/N,pxs[]->getColor(j,i+).r*N/),
Normal(nla*pxs[]->getColor(j,i).r,nla*pxs[]->getColor(j,i).g,nla*pxs[]->getColor(j,i).b),
Normal(nla*pxs[]->getColor(j+,i).r,nla*pxs[]->getColor(j+,i).g,nla*pxs[]->getColor(j+,i).b),
Normal(nla*pxs[]->getColor(j,i+).r,nla*pxs[]->getColor(j,i+).g,nla*pxs[]->getColor(j,i+).b)
);
create_a_tri(
Point((j++lamda*pxs[]->getColor(j+,i+).r)*LL/N,(i++lamda* pxs[]->getColor(j+,+i).r)*LL/N,pxs[]->getColor(j+,i+).r*N/),
Point((j++lamda*pxs[]->getColor(j+,i).r)*LL/N,(i+lamda* pxs[]->getColor(j+,i).r)*LL/N,pxs[]->getColor(j+,i).r*N/),
Point((j+lamda*pxs[]->getColor(j,i+).r)*LL/N,(i++lamda* pxs[]->getColor(j,i+).r)*LL/N,pxs[]->getColor(j,i+).r*N/),
Normal(nla*pxs[]->getColor(j+,i+).r,nla*pxs[]->getColor(j+,i+).g,nla*pxs[]->getColor(j+,i+).b),
Normal(nla*pxs[]->getColor(j+,i).r,nla*pxs[]->getColor(j+,i).g,nla*pxs[]->getColor(j+,i).b),
Normal(nla*pxs[]->getColor(j,i+).r,nla*pxs[]->getColor(j,i+).g,nla*pxs[]->getColor(j,i+).b)
);
}
}
pbrtAttributeEnd();
pbrtWorldEnd();
pbrtCleanup();
return ;
}

虽然这样效率比较低,但是至少可以脱手了。

每帧我们计算完动画,自动调用render函数,就可以在无人看守下出图了。

最后,关于pbrt中关于图形学的内容就不细说了,东西都写在pbrt里面,uber材质是一种综合材质。

放2张图结尾吧:

【pbrt】在c++程序中使用pbrt进行渲染的更多相关文章

  1. C# 程序中嵌入百度地图

    本例是对WinForm中使用百度地图的简要介绍.百度地图目前支持Android开发,IOS开发,Web开发,服务接口,具体可以参照'百度地图开放平台'. [动态加载百度地图]涉及到的知识点: WebB ...

  2. 在DevExpress程序中使用GridView直接录入数据的时候,增加列表选择的功能

    在我上篇随笔<在DevExpress程序中使用Winform分页控件直接录入数据并保存>中介绍了在GridView以及在其封装的分页控件上做数据的直接录入的处理,介绍情况下数据的保存和校验 ...

  3. Android程序中--不能改变的事情

    有时,开发人员会对应用程序进行更改,当安装为以前版本的更新时出现令人惊讶的结果 - 快捷方式断开,小部件消失或甚至根本无法安装. 应用程序的某些部分在发布后是不可变的,您可以通过理解它们来避免意外. ...

  4. 在.NET Core控制台程序中使用依赖注入

    之前都是在ASP.NET Core中使用依赖注入(Dependency Injection),昨天遇到一个场景需要在.NET Core控制台程序中使用依赖注入,由于对.NET Core中的依赖注入机制 ...

  5. 网页或微信小程序中使元素占满整个屏幕高度

    在项目中经常要用到一个容器元素占满屏幕高度和宽度,然后再在这个容器元素里放置其他元素. 宽度很简单就是width:100% 但是高度呢,我们知道的是height:100%必须是在父元素的高度给定了的情 ...

  6. 在DevExpress程序中使用Winform分页控件直接录入数据并保存

    一般情况下,我们都倾向于使用一个组织比较好的独立界面来录入或者展示相关的数据,这样处理比较规范,也方便显示比较复杂的数据.不过在一些情况下,我们也可能需要直接在GridView表格上直接录入或者修改数 ...

  7. 在DevExpress程序中使用内置的图标构建美观的界面元素

    在我们一般的程序中,为一般的界面元素添加一定的图标展示,有助于提升界面的整体的美观.结合排版布局,以及固定场景的图标,往往给用户非常好的直观感受:统一.美观.易理解.因此在一般的程序界面中,都尽量在略 ...

  8. 在DevExpress程序中使用条形码二维码控件,以及进行报表打印处理

    在很多业务系统里面,越来越多涉及到条形码.二维码的应用了,不管在Web界面还是WInform界面都需要处理很多物料相关的操作,甚至很多企业为了减少录入错误操作,为每个设备进行条形码.二维码的标签,直接 ...

  9. 在DevExpress程序中使用SplashScreenManager控件实现启动闪屏和等待信息窗口

    在我很早的WInform随笔<WinForm界面开发之"SplashScreen控件">有介绍如何使用闪屏的处理操作,不过那种是普通WInform和DevExpress ...

随机推荐

  1. input输入框中只能输入数字,非数字字符自动清除

    前言:项目中有个缴纳保证金的功能,要是输入框只能输入数字,不能输入其他字符. ①HTML代码:<input class="input-box" type="text ...

  2. javaagent笔记及一个基于javassit的应用监控程序demo

    javaagent基本用法 定义入口premain public static void premain(String agentArgs, Instrumentation inst) { Syste ...

  3. html锚点(mao dian)--特殊的超链接

    锚点(anchor):其实就是超链接的一种,一种特殊的超链接 普通的超链接,<a href="路径"></a> 是跳转到不同的页面 而锚点,<a hr ...

  4. Linux Kernel文件系统写I/O流程代码分析(二)bdi_writeback

    Linux Kernel文件系统写I/O流程代码分析(二)bdi_writeback 上一篇# Linux Kernel文件系统写I/O流程代码分析(一),我们看到Buffered IO,写操作写入到 ...

  5. liunx下查看日志最实用命令和方法

      1.业务系统访问量不是很大的时候,使用这个,有bug的地方操作下,直接看最后操作的日志,就是你刚才操作的地方,好好查bug吧 tail  -fn100  catalina.log   查询日志尾部 ...

  6. 项链(burnside)

    Description 有一个长度为 \(n\) 的项链,首尾相接形成环,现在你要给每一个位置一个颜色 \([1,m]\), 求所有不同的项链个数(可以通过旋转变成一样的称为相同) Solution ...

  7. [转]Work With Odata in Web API: Create Your First Odata Service

    本文转自:http://www.c-sharpcorner.com/UploadFile/dacca2/work-with-odata-in-web-api-create-your-first-oda ...

  8. (Forward)5 Public Speaking Tips That'll Prepare You for Any Interview

    Landing a job interview is incredibly exciting –- and often terrifying. But fear not. There are clev ...

  9. BZOJ4698: Sdoi2008 Sandy的卡片(后缀数组 二分)

    题意 题目链接 Sol 不要问我为什么发两篇blog,就是为了骗访问量 后缀数组的也比较好想,先把所有位置差分,然后在height数组中二分就行了 数据好水啊 // luogu-judger-enab ...

  10. hdu 3613 Best Reward (manachar算法)

    Best Reward Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Prob ...