Closed Fences
计算几何,借鉴了很多,写的很辛苦,因为需要考虑不少特殊情况。用二分法分割篱笆来探索每个篱笆能不能被看到,注意要设置一个分段长度的下限。在判断线段是否相交时有一些技巧。借鉴了一下混合积的方法,发现不能处理两个线段共线的情况。我判断共线和共线情况下线段是否相交的方法如下:设坐标系oxyz中两个线段ab和cd处于oxy平面上,若ab×ac与ab×ad的z分量都为0则abcd共线。共线的情况下,若ac*ad>0且bc*bd>0,则两线段不相交,否则相交。
/* ID: huilong1 LANG: C++ TASK: fence4 */ #include<stdio.h> #include<stdlib.h> #include<math.h> #define ESP 1e-4 FILE *fin,*fout; typedef struct{ double x; double y; }Point; typedef struct{ Point a; Point b; }Seg; typedef struct{ double x; double y; double z; }Vector; int N; Seg fence[201]; Point obser; Point corner[201]; Seg visible[201]; int numsee; //判断是否是相邻的篱笆 bool neighbor(int i,int j){ if(i==0&&j==N-1||i==N-1&&j==0)return true; int abs=i-j; if(abs<0)abs=-abs; if(abs==1)return true; return false; } //获得中点 Point getmiddle(Point a,Point b){ Point m; m.x=(a.x+b.x)/2; m.y=(a.y+b.y)/2; return m; } //算点积 double dotpro(Vector a,Vector b){ return a.x*b.x+a.y*b.y+a.z*b.z; } //算叉积 Vector crosspro(Vector a,Vector b){ Vector t; t.x=a.y*b.z-a.z*b.y; t.y=a.z*b.x-a.x*b.z; t.z=a.x*b.y-a.y*b.x; return t; } //获得两点决定的向量 Vector getvector(Point a,Point b){ Vector t; t.x=b.x-a.x; t.y=b.y-a.y; t.z=0; return t; } //判断线段是否交叉 bool cross(Seg p,Seg q){ Vector pv=getvector(p.a,p.b); Vector qv=getvector(q.a,q.b); Vector paqa=getvector(p.a,q.a); Vector paqb=getvector(p.a,q.b); Vector pbqa=getvector(p.b,q.a); Vector pbqb=getvector(p.b,q.b); Vector qapa=getvector(q.a,p.a); Vector qapb=getvector(q.a,p.b); //判断两线段是否共线 Vector v1=crosspro(pv,paqa); Vector v2=crosspro(pv,paqb); if(v1.z==v2.z&&v1.z==0){//共线 if(dotpro(paqa,paqb)>0&&dotpro(pbqa,pbqb)>0)return false; else return true; } //不共线 if(dotpro(v1,v2)>0)return false; if(dotpro(crosspro(qv,qapa),crosspro(qv,qapb))>0)return false; return true; } //判断是否能看到点 int seepoint(Point p,int ind){ Seg view; view.a=obser; view.b=p; for(int i=0;i<N;i++){ if(i==ind)continue; if(cross(view,fence[i]))return i; } return -1; } //判断线段是否收缩为一个点 bool ispoint(Seg s){ Point a=s.a; Point b=s.b; if((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)<ESP)return true; return false; } //判断是否可见 bool cansee(Seg s,int ind){ if(ispoint(s))return false; Point a=s.a; Point b=s.b; Point mid=getmiddle(a,b); int sta,stb,stmid; sta=seepoint(a,ind); stb=seepoint(b,ind); stmid=seepoint(mid,ind); if(sta==stb&&sta>=0)return false; if(sta<0||stb<0||stmid<0)return true; Seg t; t.a=a,t.b=mid; if(cansee(t,ind))return true; t.a=mid,t.b=b; if(cansee(t,ind))return true; return false; } int main(){ fin=fopen("fence4.in","r"); fout=fopen("fence4.out","w"); fscanf(fin,"%d",&N); fscanf(fin,"%lf %lf",&obser.x,&obser.y); for(int i=0;i<N;i++){ fscanf(fin,"%lf %lf",&corner[i].x,&corner[i].y); } //按顺序生成篱笆 for(int i=0;i<=N-2;i++){ fence[i].a=corner[i]; fence[i].b=corner[i+1]; } fence[N-1].a=corner[0]; fence[N-1].b=corner[N-1]; //判断篱笆合法性 (不相邻的不能相交) for(int i=0;i<N;i++){ for(int j=0;j<i-1;j++){ if(!neighbor(i,j)&&cross(fence[i],fence[j])){ //printf("%d %d\n",i,j); //system("pause"); fprintf(fout,"NOFENCE\n"); return 0; } } } //为了按顺序输出 调整篱笆顺序 Seg t=fence[N-1]; fence[N-1]=fence[N-2]; fence[N-2]=t; for(int i=0;i<N;i++){ if(cansee(fence[i],i)){ visible[numsee]=fence[i]; numsee++; } } fprintf(fout,"%d\n",numsee); for(int i=0;i<numsee;i++){ fprintf(fout,"%.0lf %.0lf %.0lf %.0lf\n",visible[i].a.x,visible[i].a.y,visible[i].b.x,visible[i].b.y); } //system("pause"); return 0; }
American Heritage
给一个二叉树的前序遍历和中序遍历,求后序遍历。设计一个递归式,不用重构整个二叉树。
/* ID: huilong1 LANG: C++ TASK: heritage */ #include<stdio.h> #include<stdlib.h> FILE *fin,*fout; char in[26]; int hashin[26]; char pre[26]; int num; void showpost(int prebeg,int preend,int inbeg,int inend){ if(prebeg>preend)return; showpost(prebeg+1, prebeg+hashin[pre[prebeg]-'A']-inbeg, inbeg, hashin[pre[prebeg]-'A']-1); showpost(prebeg+hashin[pre[prebeg]-'A']-inbeg+1, prebeg+hashin[pre[prebeg]-'A']-inbeg+inend-hashin[pre[prebeg]-'A'], hashin[pre[prebeg]-'A']+1, inend); fprintf(fout,"%c",pre[prebeg]); return; } int main(){ fin=fopen("heritage.in","r"); fout=fopen("heritage.out","w"); while(fscanf(fin,"%c",&in[num])&&in[num]!='\n'){ hashin[in[num]-'A']=num; num++; } int i; for(i=0;i<num;i++)fscanf(fin,"%c",&pre[i]); showpost(0,num-1,0,num-1); fprintf(fout,"\n"); //system("pause"); return 0; }
Electric Fence
用解析法解决了,后来发现可以简单地使用皮克公式。
Raucous Rockers
背包问题的变形,我加了一维用来表示唱片的数量。用dp[m][t][n]表示前m张唱片,最后一张唱片剩下t分钟,从前n首歌中选歌时可以选的最大歌曲数。则有:
dp[m][t][n]=max{ dp[m][t][n-1],
dp[m][t-time(n)][n-1]+1 ( if time(n)<=t ),
dp[m-1][T-time(n)][n-1]+1 ( if time(n)>t )
}
皮克公式(Pick's theorem)
给定顶点座标均是整点(或正方形格点)的简单多边形,皮克定理说明了其面积A和内部格点数目i、边上格点数目b的关系:A = i + b/2 - 1。
观察线段的二分法
以下出自(http://www.cppblog.com/jericho/archive/2011/01/19/138837.html)
设观察点为O,取线段AB,及AB中点C,连结AO、BO、CO;
1. 枚举所有线段,若AO、BO与同一条线段相交,则AB看不到。
2. 若A、B很近,则AB可以看成质点,AB看不到。
3. 枚举所有线段,分别与AO、BO、CO求是否相交,循环结束后判断是否有一条线段(AO、BO、CO)没有相交,如果有,则AB可以看见。
4. 二分AB, 对AC、BC进行上述过程。