程序设计竞赛题目.docx
《程序设计竞赛题目.docx》由会员分享,可在线阅读,更多相关《程序设计竞赛题目.docx(13页珍藏版)》请在冰豆网上搜索。
程序设计竞赛题目
NEERC2006
ProblemA:
ASCIIArt
[Description]
在w*h网格平面内,按顺时针给出一个含n个顶点且都在格点上的简单多边形(不自交),统计每个格子被覆盖的面积的状况.(w,h,n<=100)
[Solution]
枚举指定的格子,再与多边形求交比较麻烦,复杂度也高.实际上,可以对多边形的面积进行梯形剖分,即每条边的端点向x轴做投影,投影点与端点组成的梯形有向面积之和,就可得原面积.由于题目中是按顺时针输入的,则从右向左的有向线段对应的梯形面积为负,从左向右则为正.如左图,对于三角形ABC,其面积为=-AEDB(Green)+BDFC(All)-CFEA(Red).
接下来处理每个梯形,枚举被梯形覆盖的每列格子,按列分别处理.对于每列,一定存在被线段穿过的格子的连续段,如右图的Yellow部分;它的下面都是被完全覆盖的,如右图的Red部分.可以直接处理掉Red部分.对于每一个Yellow部分中的格子,实际上就是做一个规模为4的平面交,若不考虑特殊性,本质上只有以下2种情况(对边,邻边),讨论一下即可.实际上,我不想那么麻烦,写了个凸包算面积.
总复杂度为O(n*w*h).
ProblemB:
BillingTables
[Description]
电话号码为11位的数字串,按优先级给定一系列区间,每个区间有一个标志.若一个电话号码在被某个区间包含(多个,则取优先级高的),则电话号码就具有该区间的标志.现在要求输出用前缀表示的带标志序列.使得电话号码的前缀被匹配,则电话号码就一定具有该前缀的标志.
[Solution]
本质上是输出一棵电话号码的Trie树的叶子.每个结点表示一个电话号码前缀,若某个结点被一个区间完全包含或被几个标志一样的区间完全包含,则作为一个叶子结点,否则继续扩展.最后输出所有叶子结点即可.
每次检查结点是否被包含,需要O(n)时间.总复杂度为O(Ans*n),Ans为叶子总数.
ProblemC:
CellularAutomaton
[Description]
给定一个长度为n的序列A.定义一种操作,使得操作后的序列A'满足:
即A'i为所有和i的圆距离不超过d的元素Aj之和.问经过k次操作后的序列.(n<=500,k<=10000000,m<=1000000,d<=n/2)
[Solution]
每次操作,等价于一个矩阵乘法
:
其中n*n矩阵M为:
(上面例子中的矩阵是d=1的情况)
这样答案就是
其中矩阵的幂运算可以用倍增法在O(logk)次矩阵乘法下得出.然而每次O(n^3)的矩阵乘法还是太慢,需要改进.观察矩阵M的特殊性:
第二行是第一行右移一位,第三行是第二行右移一位,...,等等.即上行右移一位为下行.设第一行为x1,x2,...,xn,则矩阵M可以写为:
同样的,对于列也一样,左行下移一位为右行.设第一列为y1,y2,...,yn,则矩阵M可以写为:
称这样的性质为循环矩阵.易知以上两种表示法是同构的,考察两种表示法相乘的结果:
结果依然是一个循环矩阵,即一个循环矩阵乘循环矩阵还是循环矩阵.于是只要记录矩阵的第一行或第一列就可以得到整个矩阵,就是说每次乘法只要算出结果的第一行或第一列即可.乘法的时间降为O(n^2).总复杂度为O(n^2logk)
ProblemD:
DrivingDirections
[Description]
在平面上有n个平行于坐标轴的矩形障碍物,现需要将一个半径为r的圆从A点移动到B点.求最短路.(n<=30)
[Solution]
将圆靠着矩形滑动(相切),圆心的轨迹形成了一个导圆角矩形.导圆角矩形是由原矩形的4条边向外平移一个半径的距离,加上4个1/4圆作为导角.导圆角矩形的内部圆心无法进入.问题转化成,在若干导圆角矩形的障碍下,一个点移动到另一个点的最短路.
由于在曲线上点是连续的,无法使用图论算法,所以需要根据最短路的性质进行点的离散化,建立带权图.
(1)边上的点:
如下图,粗蓝色代表导圆角矩形上的完整的直线段.对于另外一个不在导圆角矩形内部的任意一点D,由三角形的性质,得到AD或CD一定比ABD,CBD短.这就说明了在直线段上除了端点A,C,没必要有其他点作为离散点.
(2)弧上的点
(2.1)如下图,对于在另一个导圆角矩形上的弧l,与弧AB的相切,切线段为CD,C,D分别为两段弧上的切点.C,D都应作为离散点.因为从A到l的最短路必经过C,D,且C,D为该路径上离开或进入导圆角矩形的事件点.
(2.2)如下图,对于不在导圆角矩形内部的任意一点D,D与弧AB相切于C.C应作为离散点.因为从B到D的路径只有过C点的路径最短,且C为离开导圆角矩形的事件点.
(2.2)
由于(2.1)会产生O(n^2)个离散点,O(n^2)条边;接着(2.2)会在(2.1)的基础上产生(n^3)的离散点,O(n^3)条边.实际上,在(2.1)中,点和边是共生的,即在产生边的过程中,若该边与障碍相交,则需要取消离散点.判断边与障碍相交需要O(n),建图用时O(n^4).
最后运行Dijkstra算法即可,复杂度为O((V+E)logV)=O(n^3logn).
ProblemE:
Exchange
[Description]
卖订单SellOrder含:
供应物品个数,价格.
买订单BuyOrder含:
需物品个数,出价.
要求写一个交易系统.对于当前买订单,若当前价格最低的卖订单低于当前出价,则发生交易.对于当前卖订单,若当前出价最高的买订单,高于当前价格,则发生交易.当出价或价格相同时,按订单的先后循序发生交易.发生交易时,按供需物品个数的最小值交易.交易后,需要修改订单的供需物品个数.订单可以取消.
共3种命令:
买订单BUY,卖订单SELL,取消订单CANCEL.共n个命令.
每个命令后,输出当前处于最低价格的物品总数和当前处于最高出价的物品总数.
(n<=10000)
[Solution]
每次要取最小或最大,所以订单队列是一个优先队列.用最大堆模拟买订单,用最小堆模拟卖订单.直接模拟.总复杂度为O(nlogn).
ProblemF:
Fool'sGame
[Description]
[Solution]
ProblemG:
Graveyard
[Description]
长度为10000的圆上,等距分布着n个点.现在再加入m个点.要使得n+m个点在圆在等距分布.这就可能需要移动原来的n个点到新位置上,要求移动(沿着圆周移动)的总距离和最小.(n,m<=1000)
[Solution]
可以转化为在单位圆上每个正n边形A的顶点到内接正n+m边形B的最近顶点的距离和最短.问题就在于得出,A,B之间的角位置关系.(图左)
可以证明,存在一个最优解,A和B的顶点至少有一个顶点重合.对于任意一个顶点不重合的方案,找出A中离B最近顶点的距离最短的顶点P,P离B的顶点Q最近.从P向Q微移一个无限小量.不少于一半的A的点都向自己的B的最近顶点前进了一个无限小量.
所以令A,B其中一个点重合,很容易就算出答案了.总复杂度O(n).
ProblemH:
HardLife
[Description]
给出无向图G(V,E),求一个子图G'(V',E'),使得比值|E'|/|V'|最大.(|V|<=100,|E|<=1000)
[Solution]
本题可以描述为:
其中Xv,Ye的取值为0或1,表示点或边是否选取.这是一个0-1分数规划(0-1fractionalprogramming).可以通过如下参数搜索的方法解决.关于答案
构造一个新问题
:
设原问题的最优解是
显然有
.设两个变量
<
.由于
>=0,把
的最优解向量
代入
中,必会得到一个更大的z值,所以z单调递减.所以有:
这就意味着可以进行二分搜索答案
.注意初始范围:
.关于01分数规划参见[1]或最优比率生成树的解法.
这样将问题转化为求
的最大值.它需要满足限制:
边(u,v)的选取,需要点u,v的选取.这样的依赖关系与函数的形式使我们想到了最小割来解决这类问题,参见[2].建立二分图,X部的点Xv代表点v,Y部的点Ye代表边e.若边e关联了2个点u,v,则分别从Xu,Xv向Ye连一条容量为正无限的边.再建立源S,汇T.由S向每个Xv连容量为λ的边,由每个Ye向T连容量为1的边.这样求出该网络的最小割Mincut.则
.
求最小割的过程使用最大流的EdmondsKarp算法.点数为O(|V|+|E|),边数为O(|E|).每次找增广轨需O(|E|).共增广O(|E|^2)次.算法总复杂度为O(|E|^3logR).而实际情况会比这个估计好很多.稀疏二分图的增广次数达不到O(|E|^2).这题使用Relabeltofront的PreflowPush算法O(|E|^3),反而会超时.
另一解:
直接保留原图,即若原图存在边(u,v),从Xu到Xv连一条容量为正无限的边,同时从Xv到Xu也连一条容量为正无限的边.再建立源S,汇T.由S向每个Xv连容量为m的边,由每个Xv向T连容量为m-Dv+2λ的边.其中Dv表示点v的度.若此网络的最小割,即为z.由于点数为O(|V|),边数为O(|E|).使用PreflowPush的复杂度为O(|V|^3),效果很好.
[Reference]
[1]T.Matsui,Y.Saruwatari,MaikoShigeno,AnAnalysisofDinkelbach'sAlgorithmfor0-1FractionalProgrammingProblems(1992)
[2]TheIntroductiontoAlgorithm,Problems26-3:
Spaceshuttleexperiments.
ProblemI:
Interconnect
[Description]
给出无向图G(V,E).每次操作任意加一条非自环的边(u,v),每条边的选择是等概率的.问使得G连通的期望操作次数.(|V|<=30,|E|<=1000)
[Solution]
图的状态的表示:
由于每次加边是等概率的,且只需要考虑图的连通性,每次加边要么不改变连通性,要么把两个连通块合并.只要把图的连通状况表示出来.于是,状态就是每个连通块的大小的集合.即
.其中Ci为每个连通块,共k个连通块.这样的状态有多少个呢?
实际上就是对应n的整数拆分的个数.f(n,k)表示把n拆分为k份的个数,有:
由上式得到:
f(30,30)=5604.状态数不多.
记当前状态为
由于点的标号是没有关系的,为避免状态重复,规定
.设
表示状态S到完全连通的期望步数.即边界
.有如下方程成立:
其中
表示连通块Ci,Cj合并后的状态.
表示使连通块Ci,Cj合并的可能加边的个数.
表示总可能加边数.其中
表示使状态S不改变的可能加边的个数.该方程等式右边有两个部分:
每次加边要么不改变连通性,要么把两个连通块合并.S与S'是满足拓补序的,即g(S')不会依赖g(S).这样就可以动态规划.最后关于g(S)解一元一次方程即可.实现上,可以采用记忆化搜索,使用Hash表存状态.复杂度为O(f(n,n)*n^2).
ProblemJ:
JavavsC++
[Description]
Java风格的变量:
单词之间无分隔符;除了首单词全小写外,所有单词的首字母大写,其他字母小写.如:
javaIdentifier,longAndMnemonicIdentifier,name,nEERC.
C++风格的变量:
单词之间以下划线"_"分隔;所有字母小写.cidentifier,longandmnemonicidentifier,name,n_e_e_r_c.
要求对于给出的变量风格相互转化,或报错.
[Solution]
若即含下划线又含大写字母或非法字符,报错.
若以下划线"_"开头或结尾,或出现连续的下划线"__",报错.
经过以上判断后,一定是合法的.
含下划线的,就是C++风格.否则就是Java风格.模拟即可.
ProblemK:
Kickdown
[Description]
给出2个长度不超过100且列高度只为1或2的段.需要将它们放入一个高度为3的容器,问能够容纳它们的最短容器长度.如将左图的两个段,镶嵌后,放入右图的容器中.
[Solution]
枚举两个段的相对位置,可以固定一个段,枚举另一个段关于固定段的偏移量.确定位置后,用O(n)时间检查两个段是否可以镶嵌在一个高度为3的容器中.最后取合法方案中最短长度的一个.总复杂度为O(n^2).