ACM必做50题的解题-最小生成树
BZOJ Solution—ACM题解
1013: [JSOI2008]球形空间产生器 sphere
这道题主要是构造出方程组,然后用高斯消元。我们设输入矩阵的第 i 行第 j 列的元素值为 ai , j ,我们所求的
第 2 页 共 96 页
2 坐标为 xi。则: ∀2 ≤ k ≤ n + 1 ,有 ∑ ( xi − a1,i ) = ∑ ( xi − ak ,i ) ,整理得 ∑ 2 xi × ( ak ,i − a1,i ) = ∑ ( ak2,i − a1, i ) 。接下来 2 2 i =1 i =1 i =1 i =1
Nim 博弈问题。我们分两种情况,如果全是 1 的话,如果是奇数个则是 Brother 否则是 John。如果不全是 1 的话,记 p 为所有石子数量的 xor 和,若 p 为 0 则是 Brother 否则是 John。
1024: [SCOI2009]生日快乐
爆搜就行了,以长为例,设长为 a,分成 k 分,那么分点一定是 ia/k。
1004: [HNOI2008]Cards
1, 2,..., n 我们对这 m+1 种情况分别计算答案, 由 Burnside 设题目一共给出了 m 种置换, 我们再加上一个 , 1, 2,..., n
引理知,最终的结果为
∑ f (i )
i =1
m +1
m +1
,其中 f(i)表示第 i 种置换的不动点个数。我们设第 i 种置换共有 T 个循环,则对
g[i ] i− f [i ] 2 × + = m[i + T ] g[i ] j m [ i ] i +T − 2
m[ j ] ∑ j = g [ i ]+1 i + T −
ACM必须掌握的算法
ACM必须的算法1.最短路(Floyd、Dijstra,BellmanFord)2.最小生成树(先写个prim,kruscal要用并查集,不好写)3.大数(高精度)加减乘除4.二分查找. (代码可在五行以内)5.叉乘、判线段相交、然后写个凸包.6.BFS、DFS,同时熟练hash表(要熟,要灵活,代码要简)7.数学上的有:辗转相除(两行内),线段交点、多角形面积公式.8. 调用系统的qsort, 技巧很多,慢慢掌握.9. 任意进制间的转换第二阶段:练习复杂一点,但也较常用的算法。
:1. 二分图匹配(匈牙利),最小路径覆盖2. 网络流,最小费用流。
3. 线段树.4. 并查集。
5. 熟悉动态规划的各个典型:LCS、最长递增子串、三角剖分、记忆化dp6.博弈类算法。
博弈树,二进制法等。
7.最大团,最大独立集。
8.判断点在多边形内。
9. 差分约束系统. 10. 双向广度搜索、A*算法,最小耗散优先.相关的知识图论:路径问题 0/1边权最短路径 BFS 非负边权最短路径(Dijkstra)可以用Dijkstra解决问题的特征负边权最短路径Bellman-Ford Bellman-Ford的Yen-氏优化差分约束系统 Floyd 广义路径问题传递闭包极小极大距离 / 极大极小距离 EulerPath / Tour 圈套圈算法混合图的 Euler Path / TourHamilton Path / Tour 特殊图的Hamilton Path / Tour 构造生成树问题最小生成树第k小生成树最优比率生成树 0/1分数规划度限制生成树连通性问题强大的DFS算法无向图连通性割点割边二连通分支有向图连通性强连通分支 2-SAT最小点基有向无环图拓扑排序有向无环图与动态规划的关系二分图匹配问题一般图问题与二分图问题的转换思路最大匹配有向图的最小路径覆盖0 / 1矩阵的最小覆盖完备匹配最优匹配稳定婚姻网络流问题网络流模型的简单特征和与线性规划的关系最大流最小割定理最大流问题有上下界的最大流问题循环流最小费用最大流 / 最大费用最大流弦图的性质和判定组合数学解决组合数学问题时常用的思想逼近递推 / 动态规划概率问题Polya定理计算几何 / 解析几何计算几何的核心:叉积 / 面积解析几何的主力:复数基本形点直线,线段多边形凸多边形 / 凸包凸包算法的引进,卷包裹法Graham扫描法水平序的引进,共线凸包的补丁完美凸包算法相关判定两直线相交两线段相交点在任意多边形内的判定点在凸多边形内的判定经典问题最小外接圆近似O(n)的最小外接圆算法点集直径旋转卡壳,对踵点多边形的三角剖分数学 / 数论最大公约数Euclid算法扩展的Euclid算法同余方程 / 二元一次不定方程同余方程组线性方程组高斯消元法解mod 2域上的线性方程组整系数方程组的精确解法矩阵行列式的计算利用矩阵乘法快速计算递推关系分数分数树连分数逼近数论计算求N的约数个数求phi(N)求约数和快速数论变换……素数问题概率判素算法概率因子分解数据结构组织结构二叉堆左偏树二项树胜者树跳跃表样式图标斜堆reap统计结构树状数组虚二叉树线段树矩形面积并圆形面积并关系结构Hash表并查集路径压缩思想的应用 STL中的数据结构vectordequeset / map动态规划 / 记忆化搜索动态规划和记忆化搜索在思考方式上的区别最长子序列系列问题最长不下降子序列最长公共子序列最长公共不下降子序列一类NP问题的动态规划解法树型动态规划背包问题动态规划的优化四边形不等式函数的凸凹性状态设计规划方向线性规划常用思想二分最小表示法串KMPTrie结构后缀树/后缀数组 LCA/RMQ有限状态自动机理论排序选择/冒泡快速排序堆排序归并排序基数排序拓扑排序排序网络中级:一.基本算法:(1)C++的标准模版库的应用. (poj3096,poj3007)(2)较为复杂的模拟题的训练(poj3393,poj1472,poj3371,poj1027,poj2706)二.图算法:(1)差分约束系统的建立和求解. (poj1201,poj2983)(2)最小费用最大流(poj2516,poj2516,poj2195)(3)双连通分量(poj2942)(4)强连通分支及其缩点.(poj2186)(5)图的割边和割点(poj3352)(6)最小割模型、网络流规约(poj3308, )三.数据结构.(1)线段树. (poj2528,poj2828,poj2777,poj2886,poj2750)(2)静态二叉检索树. (poj2482,poj2352)(3)树状树组(poj1195,poj3321)(4)RMQ. (poj3264,poj3368)(5)并查集的高级应用. (poj1703,2492)(6)KMP算法. (poj1961,poj2406)四.搜索(1)最优化剪枝和可行性剪枝(2)搜索的技巧和优化 (poj3411,poj1724)(3)记忆化搜索(poj3373,poj1691)五.动态规划(1)较为复杂的动态规划(如动态规划解特别的施行商问题等)(poj1191,poj1054,poj3280,poj2029,poj2948,poj1925,poj3034)(2)记录状态的动态规划. (POJ3254,poj2411,poj1185)(3)树型动态规划(poj2057,poj1947,poj2486,poj3140)六.数学(1)组合数学:1.容斥原理.2.抽屉原理.3.置换群与Polya定理(poj1286,poj2409,poj3270,poj1026).4.递推关系和母函数.(2)数学.1.高斯消元法(poj2947,poj1487,poj2065,poj1166,poj1222)2.概率问题. (poj3071,poj3440)3.GCD、扩展的欧几里德(中国剩余定理) (poj3101)(3)计算方法.1.0/1分数规划. (poj2976)2.三分法求解单峰(单谷)的极值.3.矩阵法(poj3150,poj3422,poj3070)4.迭代逼近(poj3301)(4)随机化算法(poj3318,poj2454)(5)杂题.(poj1870,poj3296,poj3286,poj1095)七.计算几何学.(1)坐标离散化.(2)扫描线算法(例如求矩形的面积和周长并,常和线段树或堆一起使用).(poj1765,poj1177,poj1151,poj3277,po j2280,poj3004)(3)多边形的内核(半平面交)(poj3130,poj3335)(4)几何工具的综合应用.(poj1819,poj1066,poj2043,poj3227,poj2165,poj3429)高级:一.基本算法要求:(1)代码快速写成,精简但不失风格(poj2525,poj1684,poj1421,poj1048,poj2050,poj3306)(2)保证正确性和高效性. poj3434二.图算法:(1)度限制最小生成树和第K最短路. (poj1639)(2)最短路,最小生成树,二分图,最大流问题的相关理论(主要是模型建立和求解)(poj3155,poj2112,poj1966,poj3281,poj1087,poj2289,poj3216,poj2446(3)最优比率生成树. (poj2728)(4)最小树形图(poj3164)(5)次小生成树.(6)无向图、有向图的最小环三.数据结构.(1)trie图的建立和应用. (poj2778)(2)LCA和RMQ问题(LCA(最近公共祖先问题) 有离线算法(并查集+dfs) 和在线算法(RMQ+dfs)).(poj1330)(3)双端队列和它的应用(维护一个单调的队列,常常在动态规划中起到优化状态转移的目的). (poj2823)(4)左偏树(可合并堆).(5)后缀树(非常有用的数据结构,也是赛区考题的热点).(poj3415,poj3294)四.搜索(1)较麻烦的搜索题目训练(poj1069,poj3322,poj1475,poj1924,poj2049,poj3426)(2)广搜的状态优化:利用M进制数存储状态、转化为串用hash表判重、按位压缩存储状态、双向广搜、A*算法.(poj1768,poj1184,poj1872,poj1324,poj2046,poj1482)(3)深搜的优化:尽量用位运算、一定要加剪枝、函数参数尽可能少、层数不易过大、可以考虑双向搜索或者是轮换搜索、IDA*算法. (poj3131,poj2870,poj2286)五.动态规划(1)需要用数据结构优化的动态规划.(poj2754,poj3378,poj3017)(2)四边形不等式理论.(3)较难的状态DP(poj3133)六.数学(1)组合数学.1.MoBius反演(poj2888,poj2154)2.偏序关系理论.(2)博奕论.1.极大极小过程(poj3317,poj1085)2.Nim问题.七.计算几何学.(1)半平面求交(poj3384,poj2540)(2)可视图的建立(poj2966)(3)点集最小圆覆盖.(4)对踵点(poj2079)八.综合题.(poj3109,poj1478,poj1462,poj2729,poj2048,poj333 6,poj3315,poj2148,poj1263)初期:一.基本算法:(1)枚举. (poj1753,poj2965) (2)贪心(poj1328,poj2109,poj2586)(3)递归和分治法. (4)递推.(5)构造法.(poj3295) (6)模拟法.(poj1068,poj2632,poj1573,poj2993,poj2996)二.图算法:(1)图的深度优先遍历和广度优先遍历.(2)最短路径算法(dijkstra,bellman-ford,floyd,heap+dijkstra)(poj1860,poj3259,poj1062,poj2253,poj1125,po j2240)(3)最小生成树算法(prim,kruskal)(poj1789,poj2485,poj1258,poj3026)(4)拓扑排序 (poj1094)(5)二分图的最大匹配 (匈牙利算法) (poj3041,poj3020)(6)最大流的增广路算法(KM算法). (poj1459,poj3436)三.数据结构.(1)串 (poj1035,poj3080,poj1936)(2)排序(快排、归并排(与逆序数有关)、堆排)(poj2388,poj2299)(3)简单并查集的应用.(4)哈希表和二分查找等高效查找法(数的Hash,串的Hash)(poj3349,poj3274,POJ2151,poj1840,poj2002,po j2503)(5)哈夫曼树(poj3253)(6)堆(7)trie树(静态建树、动态建树) (poj2513)四.简单搜索(1)深度优先搜索(poj2488,poj3083,poj3009,poj1321,poj2251)(2)广度优先搜索(poj3278,poj1426,poj3126,poj3087.poj3414)(3)简单搜索技巧和剪枝(poj2531,poj1416,poj2676,1129)五.动态规划(1)背包问题. (poj1837,poj1276)(2)型如下表的简单DP(可参考lrj的书 page149):1.E[j]=opt{D+w(i,j)}(poj3267,poj1836,poj1260,poj2533)2.E[i,j]=opt{D[i-1,j]+xi,D[i,j-1]+yj,D[i-1][j-1 ]+zij} (最长公共子序列)(poj3176,poj1080,poj1159)3.C[i,j]=w[i,j]+opt{C[i,k-1]+C[k,j]}.(最优二分检索树问题)六.数学(1)组合数学:1.加法原理和乘法原理.2.排列组合.3.递推关系.(POJ3252,poj1850,poj1019,poj1942)(2)数论.1.素数与整除问题2.进制位.3.同余模运算.(poj2635, poj3292,poj1845,poj2115)(3)计算方法.1.二分法求解单调函数相关知识.(poj3273,poj3258,poj1905,poj3122)七.计算几何学.(1)几何公式.(2)叉积和点积的运用(如线段相交的判定,点到线段的距离等). (poj2031,poj1039)(3)多边型的简单算法(求面积)和相关判定(点在多边型内,多边型是否相交)(poj1408,poj1584)(4)凸包. (poj2187,poj1113)。
acm常用板子题
acm常用板子题
ACM常用模板题包括但不限于:
字符串操作:如字符串匹配、字符串排序、字符串还原等题目,需要熟练掌握字符串的基本操作和常用算法。
数组操作:如数组排序、数组查找、数组分割等题目,需要熟练掌握数组的基本操作和常用算法。
树形结构:如二叉树、AVL树、红黑树等题目,需要熟练掌握树形结构的基本操作和常用算法。
图论算法:如最短路径、最小生成树、拓扑排序等题目,需要熟练掌握图论算法的基本操作和常用算法。
动态规划:如背包问题、最长公共子序列、最长递增子序列等题目,需要熟练掌握动态规划的基本操作和常用算法。
搜索算法:如深度优先搜索、广度优先搜索等题目,需要熟练掌握搜索算法的基本操作和常用算法。
数据结构:如哈希表、并查集、线段树等题目,需要熟练掌握数据结构的基本操作和常用算法。
以上是一些常见的ACM模板题,当然还有很多其他的题目类型。
要提高自己的ACM水平,需要多做题、多思考、多总结,不断拓宽自己的算法和数据结构知识面。
算法竞赛入门经典训练指南题单
算法竞赛入门经典训练指南题单全文共四篇示例,供读者参考第一篇示例:算法竞赛作为计算机科学领域中的重要领域之一,一直备受关注和推崇。
参加算法竞赛可以帮助我们提高编程能力、思维灵活性和解决问题的能力。
而且,通过算法竞赛,我们还可以结识来自各个国家的优秀程序员,开阔自己的视野,提高自己的竞争力。
而要在算法竞赛中取得好成绩,就需要有一定的训练和积累。
本文将为大家推荐一些经典的算法竞赛训练题单,希望能帮助大家快速入门和提升自己的算法竞赛水平。
1. ACM-ICPC题单ACM国际大学生程序设计竞赛(ACM-ICPC)是全球规模最大、最具影响的大学生程序设计竞赛,被誉为程序设计界的“奥林匹克”。
ACM-ICPC赛题难度较高,对参赛者的编程能力、算法设计能力和团队协作能力等方面都有严格的要求。
参加ACM-ICPC的同学们需要有一定的训练和备战。
以下是一些经典的ACM-ICPC训练题单,推荐给大家:1、CodeforcesCodeforces是一个国际知名的在线编程比赛和训练平台,其比赛难度较高,同时也有很大的影响力。
在Codeforces上,你可以找到各种难度的题目,从入门级到专家级都有覆盖。
推荐大家在Codeforces 上刷题,提高自己的编程能力和解题能力。
3、洛谷洛谷是国内著名的在线题库和训练平台,里面汇集了大量的ACM 竞赛题目和OJ题目,适合广大程序员练习和提升编程能力。
洛谷上的题目分类清晰,难度适中,非常适合新手入门和提高。
2. Google Code Jam题单Google Code Jam是由谷歌主办的一项全球性的编程大赛,是程序员们展示自己编程才华的绝佳舞台。
Google Code Jam的题目设计独特,难度适中,涵盖了很多经典的算法问题,非常适合有一定编程基础的程序员练习和挑战。
以下是一些推荐的Google Code Jam题单:LeetCode是一个在线的编程练习平台,里面包含了大量的算法和数据结构问题,适合练习和提升自己的编程能力。
北大ACM题型
3.C[i,j]=w[i,j]+opt{C[i,k-1]+C[k,j]}.(最优二分检索树问题)
六.数学
(1)组合数学:
1.加法原理和乘法原理.
2.排列组合.
3.递推关系. (POJ3252,poj1850,poj1019,poj1942)
(2)数论.
1.素数与整除问题
(2)扫描线算法(例如求矩形的面积和周长并,常和线段树或堆一起使用). (poj1765,poj1177,poj1151,poj3277,poj2280,poj3004)
(3)多边形的内核(半平面交)(poj3130,poj3335)
(4)几何工具的综合应用.(poj1819,poj1066,poj2043,poj3227,poj2165,poj3429)
(2)记录状态的动态规划. (POJ3254,poj2411,poj1185)
(3)树型动态规划(poj2057,poj1947,poj2486,poj3140)
六.数学
(1)组合数学:
1.容斥原理.
2.抽屉原理.
3.置换群与Polya定理(poj1286,poj2409,poj3270,poj1026).
五.动态规划
(1)需要用数据结构优化的动态规划. (poj2754,poj3378,poj3017)
(2)四边形不等式理论.
(3)较难的状态DP(poj3133)
六.数学
(1)组合数学.
1.MoBius反演(poj2888,poj2154)
2.偏序关系理论.
(2)博奕论.
----------------------------------------------------------------------------------------------- -----------------------------------补充 Dp状态设计与方程总结
最小生成树的概念
最小生成树的概念
在图论中,最小生成树是一个连通图的生成树,其边的权值之和最小。
通俗地说,最
小生成树是指在一个图中找到一棵权值最小的生成树,这个生成树包含了连通图的所有顶点,且边的数量最小。
怎么找到最小生成树呢?有两种常用算法:Prim算法和Kruskal算法。
Prim算法首先任选一个点作为起点,然后在剩余的点中选择与当前集合距离最短的点加入集合,直到所有点被加入。
在加入每一个点时,找到与当前集合连接的距离最短的边,加入到生成树中。
重复以上步骤,直到所有点都被加入到生成树中。
Kruskal算法则是将边按照权值从小到大排序,选择权值最小的边加入到生成树中,
如果加入当前边后不构成环,则加入,否则继续找下一条权值最小的边。
重复以上步骤,
直到所有点都被加入到生成树中。
最小生成树有很广泛的应用,如在通信、传输、路网规划等领域都有很重要的作用。
在有些应用中,最小生成树不仅要求边的权值之和最小,还要满足一些约束条件,比如边
的数量、每个点的度数等,这时我们需要采用更加复杂的算法来求解问题。
最小生成树的应用非常广泛,比如在计算机网络中,路由协议需要找到最短的数据传
输路径;在城市交通中,规划出最优的交通路径能够有效减少能源的消耗;在电力系统中,设计最短的输电线路可以节省能源成本。
最小生成树的运用如此广泛,它不仅在计算机科
学中有重要作用,也在其他各个领域有着不可替代的作用。
北大 poj acm题目推荐50题
-北大poj acm题目推荐50题POJ == 北京大学ACM在线评测系统/JudgeOnline1. 标记难和稍难的题目大家可以看看,思考一下,不做要求,当然有能力的同学可以直接切掉。
2. 标记为A and B 的题目是比较相似的题目,建议大家两个一起做,可以对比总结,且二者算作一个题目。
3. 列表中大约有70个题目。
大家选做其中的50道,且每类题目有最低数量限制。
4. 这里不少题目在BUPT ACM FTP 上面都有代码,请大家合理利用资源。
5. 50个题目要求每个题目都要写总结,养成良好的习惯。
6. 这50道题的规定是我们的建议,如果大家有自己的想法请与我们Email 联系。
7. 建议使用C++ 的同学在POJ 上用G++ 提交。
8. 形成自己编写代码的风格,至少看上去美观,思路清晰(好的代码可以很清楚反映出解题思路)。
9. 这个列表的目的在于让大家对各个方面的算法有个了解,也许要求有些苛刻,教条,请大家谅解,这些是我们这些年的经验总结,所以也请大家尊重我们的劳动成果。
10. 提交要求:一个总文件夹名为bupt0xx (即你的比赛帐号), 这个文件夹内有各个题目类别的子目录(文件夹),将相应的解题报告放入对应类别的文件夹。
在本学期期末,小学期开始前,将该文件夹的压缩包发至buptacm@。
对于每个题目只要求一个POJxxxx.cpp 或POJxxxx.java (xxxx表示POJ该题题号) 的文件,注意不要加入整个project 。
11. 如果有同学很早做完了要求的题目,请尽快和我们联系,我们将指导下一步的训练。
下面是一个解题报告的范例:例如:POJ1000.cpp//考查点:会不会编程序。
//思路:此题要求输入两个数,输出两个数的和,我用scanf 和printf。
//提交情况:Wrong Answer 1次,忘了写printf()。
Compile Error 2次,选错了语言,由于C++ 和G++ 在iostream.h 的不用引用方法;少一个大括号。
ACM基础算法入门教程
ACM基础算法入门教程ACM(ACM International Collegiate Programming Contest)是国际大学生程序设计竞赛的缩写,被认为是计算机领域最有权威和最具挑战性的竞赛之一、ACM竞赛要求参赛者在规定的时间内,根据给出的问题,编写出能在规定时间内运行并给出正确答案的程序。
参加ACM竞赛不仅可以锻炼算法思维,提高编程实力,还可以拓宽知识领域和增加竞争力。
在这个ACM基础算法入门教程中,我们将介绍一些常用的基础算法和数据结构,帮助初学者更好地理解和掌握ACM竞赛所需的算法知识。
一、排序算法排序算法是ACM竞赛中最常用的算法之一,能够帮助我们按照一定的规则将数据进行排序,从而解决一些需要有序数据的问题。
1.冒泡排序:通过多次比较和交换来实现,每次迭代将最大的值沉到最底部。
2.快速排序:选择一个基准元素将数组分为两部分,一部分都小于基准元素,一部分都大于基准元素,递归排序子数组。
3.归并排序:将数组不断二分,将相邻两个子数组排序后再合并成一个有序数组。
4.插入排序:从第二个元素开始,依次将元素插入已排序的子数组中。
二、查找算法查找算法可以帮助我们在一组数据中找到目标元素,从而解决一些需要查找特定数据的问题。
1.顺序查找:逐个扫描数据,直到找到目标元素或扫描结束为止。
2.二分查找:对已排序的数组进行查找,不断将数组二分直到找到目标元素的位置。
3.哈希查找:通过计算数据的哈希值找到对应的存储位置,实现快速查找。
三、字符串匹配算法字符串匹配算法可以帮助我们在一组字符串中寻找特定模式的子字符串,从而解决一些需要在字符串中查找其中一种规律的问题。
1.暴力匹配算法:对目标字符串的每个位置,逐个将模式串进行匹配,直到找到或匹配结束为止。
2.KMP算法:通过已匹配的部分信息,尽量减少字符比较的次数。
3. Boyer-Moore算法:通过预先计算模式串中每个字符最后出现位置的表格,以及坏字符规则和好后缀规则,来实现快速匹配。
最小生成树问题
2.1 最小生成树
树T(V,E)的性质:
E 树的边数等于其顶点数减“1”,即 V 1 ; 树的任意两个顶点之间恰有一条初级链相连接; 在树中任意去掉一条边后,便得到一个不连通的 图; 在树中任意两个顶点之间添加一条新边,所得新 图恰有一个初级圈。
例如,图 6.4.1 给出的 G1 和 G2 是树,但 G3 和 G4 则不是树。
44
44 69
结果显示于图
求最小生成树的 Prim 算法
Prim 算法的直观描述 假设 T0 是赋权图 G 的最小生成树。任选一 个顶点将其涂红,其余顶点为白点;在一个端 点为红色,另一个端点为白色的边中,找一条 权最小的边涂红,把该边的白端点也涂成红色; 如此,每次将一条边和一个顶点涂成红色,直 到所有顶点都成红色为止。最终的红色边便构 成最小生成树 T0 的边集合。
在求最小生成树的有效算法中,最著名的两个是 Kruskal(克罗斯克尔)算法和 Prim(普瑞姆)算法, 其迭代过程都是基于贪婪法来设计的。 1.求最小生成树的 Kruskal 算法
Kruskal 算法的直观描述 假设 T0 是赋权图 G 的最小生成树,T0 中的边和 顶点均涂成红色,初始时 G 中的边均为白色。 ① 将所有顶点涂成红色; ② 在白色边中挑选一条权值最小的边,使其与红 色边不形成圈,将该白色边涂红; ③ 重复②直到有 n1 条红色边,这 n1 条红色边 便构成最小生成树 T0 的边集合。
最小生成树算法
一个简单连通图只要不是树,其生成树就不唯 一,而且非常多。一般地,n 个顶点地完全图,其 不同地生成树个数为 nn2。因而,寻求一个给定赋 权图的最小生成树,一般是不能用穷举法的。例如, 30 个顶点的完全图有 3028个生成树,3028 有 42 位, 即使用最现代的计算机,在我们的有生之年也是无 法穷举的。所以,穷举法求最小生成树是无效的算 法,必须寻求有效的算法。
acm贪心算法经典题型归纳
acm贪心算法经典题型归纳
贪心算法是一种在求解最优化问题时常用的算法思想,它通常
用于解决那些具有最优子结构性质的问题。
在ACM竞赛中,贪心算
法经典题型主要包括以下几类:
1. 区间调度问题,这类问题要求在一系列区间中选择尽量多的
不重叠区间。
经典问题包括最大不重叠区间数量、最小区间覆盖等。
2. 背包问题,在给定背包容量和一系列物品的重量、价值的情
况下,选择装入背包的物品,使得背包内物品的总价值最大。
贪心
算法通常用于解决部分背包问题或者分数背包问题。
3. 最小生成树,贪心算法经典的应用之一是求解最小生成树,
其中Prim算法和Kruskal算法就是典型的贪心算法。
4. 最短路径问题,在有向图或者无向图中,求解起点到终点的
最短路径。
Dijkstra算法和Bellman-Ford算法都可以使用贪心思
想进行优化。
5. 哈夫曼编码,贪心算法还可以用于构造哈夫曼树,实现数据
的最优编码。
以上仅是贪心算法在ACM竞赛中的一些经典题型,实际上贪心算法还可以应用于很多其他问题的求解中。
在解决这些问题时,需要注意贪心选择性质和最优子结构性质,合理选择贪心策略,并证明其正确性。
同时,也需要注意到贪心算法并不适用于所有问题,有时候需要结合动态规划等其他算法来求解。
希望这些信息对你有帮助。
ACM必做50题的解题-搜索
POJ1011 Sticks 搜索+强剪枝这个题目是不是贪心的,我就是第一次用了贪心,一直W A,相当的悲剧,贪心错误的sample:7 15 11 8 8 8 4 3 2 1,所以大家还是全部搜索。
但是全部搜索必须剪枝,不然肯定是TLE的,而且本体属于强剪枝,少剪了也是TLE。
经典搜索题,果然是到处充斥着剪枝才能过啊,我的代码离剪到极限还差很多题目给出一大堆小棍子的长度,需要把他们拼成几根长度相等的大棍子,求大棍子的最短长度看自己剪枝方法的效果时候,可以添设一个变量来记录递归次数如剪枝4:没有这个剪枝的情况下对以下数据需要40万次递归,而加上这个剪枝后减少到了4万多次对数据:4515 3 2 4 11 1 8 8 8 15 3 2 4 11 1 8 8 8 15 3 2 4 11 1 8 8 8 15 3 2 4 11 1 8 8 8 15 3 2 4 11 1 8 8 8#include <iostream>#include <algorithm>using namespace std;int sticks[65];int used[65];int n,len;bool dfs(int i,int l,int t)//i为当前试取的棍子序号,l为要拼成一根完整的棍子还需要的长度,t初值为所有棍子总长度{if(l==0){t-=len;if(t==0)return true;for(i=0;used[i];++i); //剪枝1:搜索下一根大棍子的时候,找到第一个还没有使用的小棍子开始used[i]=1; //由于排序过,找到的第一根肯定最长,也肯定要使用,所以从下一根开始搜索if(dfs(i+1,len-sticks[i],t))return true;used[i]=0;t+=len;}{for(int j=i;j<n;++j){if(j>0&&(sticks[j]==sticks[j-1]&&!used[j-1])) //剪枝2:前后两根长度相等时,如果前面那根没被使用,也就是由前面那根continue; //开始搜索不到正确结果,那么再从这根开始也肯定搜索不出正确结果,此剪枝威力较大if(!used[j]&&l>=sticks[j]) //剪枝3:最简单的剪枝,要拼成一根大棍子还需要的长度L>=当前小棍子长度,才能选用{l-=sticks[j];used[j]=1;if(dfs(j,l,t))return true;l+=sticks[j];used[j]=0;if(sticks[j]==l) //剪枝4:威力巨大的剪枝,程序要运行到此处说明往下的搜索失败,若本次的小棍长度刚好填满剩下长度,但是后break; //面的搜索失败,则应该返回上一层}}}return false;}bool cmp(const int a, const int b){return a>b;}int main(){while(cin>>n&&n){int sum=0;for(int i=0;i<n;++i){cin>>sticks[i];sum+=sticks[i];used[i]=0;}sort(sticks,sticks+n,cmp); //剪枝5:从大到小排序后可大大减少递归次数bool flag=false;for(len=sticks[0];len<=sum/2;++len) //剪枝6:大棍长度一定是所有小棍长度之和的因数,且最小因数应该不小于小棍中最长的长度{if(sum%len==0){if(dfs(0,len,sum)){flag=true;cout<<len<<endl;break;}}}if(!flag)cout<<sum<<endl;}return 0;}poj 1033 Defragment题意:磁盘整理,按照从第一个文件到最后一个文件的顺序排放,而且每个文件的碎片按原来的顺序放在一起,要求转移的次数最少。
最小生成树
17
D
应用举例——最小生成树
Prim算法 34 A 46 19 F B 12 26 25 E 38 cost'={(A, B)34, (C, D)17, (F, E)26}
17
U={A, F, C, D}
V-U={B, E}
cost={(A, B)34, (F, E)26}
25
C
17
D
应用举例——最小生成树
{B, C, D, E, F} (A F)19
{B, C, D, E} {B, D, E} {B, E} {B} {}
(F C)25
(C D)17
(F E)26
adjvex lowcost
adjvex lowcost
4 12
{A, F, C, D, E}
{A,F,C,D,E,B}
(E B)12
21
应用举例——最小生成树
Prim算法——伪代码
1. 将顶点0加入集合U中; 2. 初始化辅助数组shortEdge,分别为lowcost和adjvex赋值; 3. 重复执行下列操作n-1次 3.1 在shortEdge中选取最短边,取其对应的下标k; 3.2 输出边(k, shortEdge[k].adjvex)和对应的权值; 3.3 将顶点k加入集合U中; 3.4 调整数组shortEdge;
U
V-U
输出
adjvex lowcost adjvex lowcost adjvex lowcost adjvex lowcost
0 34 0 34 0 34 0 34
0 46 5 25
0 ∞ 5 25 2 17
0 ∞ 5 26 5 26 5 26
0 19
atcoder关于最小生成树的题目
AtCoder 关于最小生成树的题目:在 AtCoder 的竞赛中经常会遇到与最小生成树相关的题目,这些题目往往需要我们深入理解最小生成树的概念和算法,并能够灵活运用它们解决实际问题。
在本文中,我们将从简到繁地探讨最小生成树的概念和相关算法,并且结合 AtCoder 中的一些题目进行讲解,帮助大家更好地理解和掌握这一重要的算法。
1. 最小生成树的概念最小生成树是指一个给定的带权无向连通图中,权值之和最小的生成树。
在这里,我们需要理解带权图、连通图以及生成树的概念。
带权图是指图中每条边都带有权值,连通图是指图中任意两个顶点之间都存在路径,生成树是指一个图中包含所有顶点的树。
最小生成树可以通过 Prim 算法和 Kruskal 算法来求解,这两个算法是我们在解决AtCoder 中相关题目时常用的方法。
2. Prim 算法Prim 算法是一种贪心算法,其核心思想是以一个顶点作为起点开始,逐步选择与当前生成树相邻且权值最小的边,直到所有顶点都被包含在生成树中。
在 AtCoder 的题目中,我们可能会遇到需要使用 Prim 算法求解最小生成树的情况。
某道题目给定了一个带权无向连通图,要求我们找到其最小生成树的权值之和,这时我们就可以考虑使用Prim 算法来解决。
3. Kruskal 算法Kruskal 算法也是求解最小生成树的常用算法之一,其思想是先将图中的边按权值从小到大排序,然后依次加入权值最小且不形成环的边,直到生成树中包含所有顶点为止。
在 AtCoder 的题目中,有时会要求我们使用 Kruskal 算法求解最小生成树的权值之和,这时我们需要对题目中的边进行排序并且判断是否形成环,从而得到最小生成树的权值。
4. AtCoder 相关题目在 AtCoder 的比赛中,经常会见到一些与最小生成树相关的题目,这些题目可能涉及到图论、树的搜索和动态规划等知识。
在解决这些题目时,我们需要结合 Prim 和 Kruskal 算法来思考,同时考虑到题目背景和限制条件,灵活选择合适的算法求解。
数学建模最小生成树例题
数学建模最小生成树例题例题1:某城市计划建设一条高速公路,需要在若干个村庄之间选择一条最优路径。
已知各个村庄之间的距离,请使用最小生成树算法为高速公路选择最优路径。
参考答案:最小生成树算法可以用于解决此类问题。
常用的最小生成树算法有Kruskal算法和Prim算法。
1. Kruskal算法:按照边的权重从小到大排序,依次将边加入生成树,如果加入的边与已选择的边不构成环,则加入,否则不加入。
2. Prim算法:首先选择权重最小的边加入生成树,然后从剩余的边中选择一条与已选择的边相连且权重最小的边加入生成树,直到所有边都加入生成树。
例题2:一个通信网络由若干个节点和边组成,节点代表城市,边代表通信线路。
已知各个城市之间的距离和通信需求,请使用最小生成树算法为该通信网络设计一个最优的通信线路网。
参考答案:最小生成树算法可以用于解决此类问题。
通过最小生成树算法,我们可以找到一个包含所有节点且边的总权重最小的树形结构,以满足各个城市之间的通信需求。
常用的最小生成树算法有Kruskal算法和Prim算法。
1. Kruskal算法:按照边的权重从小到大排序,依次将边加入生成树,如果加入的边与已选择的边不构成环,则加入,否则不加入。
2. Prim算法:首先选择权重最小的边加入生成树,然后从剩余的边中选择一条与已选择的边相连且权重最小的边加入生成树,直到所有边都加入生成树。
例题3:一个城市的电力网由多个节点和边组成,节点代表发电厂或变电站,边代表输电线路。
已知各个节点之间的电抗和传输功率,请使用最小生成树算法为该城市电力网设计一个最优的输电线路。
参考答案:最小生成树算法可以用于解决此类问题。
通过最小生成树算法,我们可以找到一个包含所有节点且边的总电抗最小的树形结构,以满足各个节点之间的电力传输需求。
常用的最小生成树算法有Kruskal算法和Prim算法。
1. Kruskal算法:按照边的电抗从小到大排序,依次将边加入生成树,如果加入的边与已选择的边不构成环,则加入,否则不加入。
ACM资料
最优比率生成树
0/1分数规划
度限制生成树
连通性问题
强大的DFS算法
无向图连通性
割点
割边
二连通分支
有向图连通性
强连通分支
2-SAT
最小点基
有向无环图
拓扑排序
有向无环图与动态规划的关系
二分图匹配问题
一般图问题与二分图问题的转换思路
组合数学
解决组合数学问题时常用的思想
逼近
递推 / 动态规划
概率问题
Polya定理
计算几何 / 解析几何
计算几何的核心:叉积 / 面积
解析几何的主力:复数
基本形
点
直线,线段
多边形
凸多边形 / 凸包
凸包算法的引进,卷包裹法
数论计算
求N的约数个数
求phi(N)
求约数和
快速数论变换
……
素数问题
概率判素算法
概率因子分解
数据结构
组织结构
二叉堆
左偏树
二项树
胜者树
跳跃表
样式图标
斜堆
reap
统计结构
树状数组
虚二叉树
线段树
8. 调用系统的qsort, 技巧很多,慢慢掌握.
9. 任意进制间的转换
第二阶段:
练习复杂一点,但也较常用的算法。
如:
1. 二分图匹配(匈牙利),最小路径覆盖
2. 网络流,最小费用流。
3. 线段树.
4. 并查集。
5. 熟悉动态规划的各个典型:LCS、最长递增子串、三角剖分、记忆化dp
最小生成树问题
最小生成树问题
最小生成树问题是指在连接有n个点的图的所有n-1条边中,找到一棵边权和最小的树,这棵树包含了图中所有的点,并且所有点之间都是通过这些边相互连接的。
最小生成树问题可以用来解决一些实际问题,比如网络规划、电力传输、通信网络等。
在计算机领域中,最小生成树问题通常可以用来解决分布式系统中的数据同步问题、数据中心间的通信问题等。
常用的解决最小生成树问题的算法有Prim算法和Kruskal算法。
Prim算法是一种贪心算法,它从一个初始点开始,每次选择与当前生成树相连的边中权值最小的边,并且将该边连接的点加入到生成树中。
重复这个过程,直到生成树包含了所有的点为止。
Kruskal算法是一种基于并查集的贪心算法。
它将所有边按照权值从小到大排序,然后依次遍历每条边,如果这条边连接的两个点不在同一个连通分量中,则将这条边添加到最小生成树中,并合并这两个连通分量。
重复这个过程,直到生成树包含了所有的点为止。
最小生成树问题是一个经典的优化问题,可以使用上述的两种算法来解决。
其中Prim算法的时间复杂度为O(n^2),Kruskal
算法的时间复杂度为O(m log n),其中n表示点的个数,m表示边的个数。
ACM比赛模板
ACM比赛模板目录1.最小生成树 (4)2.最短路算法 (9)3.素数打表 (17)4.最大匹配 (18)5.线段树(敌兵布阵) (23)6.线段树(逆序树) (26)7.树形dp (29)8.树状数组(段跟新) (33)9.Kmp模板 (37)10.线段树(点跟新) (46)11.强连通 (50)12.最小割 (55)13.单源最短路(spfa) (62)14.三分查找 (66)15.字典树(统计难题) (68)16.最大流入门题1273 (72)17.状态压缩 (75)18.匈牙利(HDU 2063)(最大匹配) (77)19.凸包(HDU1348) (79)20.树状数组(HDU1166) (82)21.强连通 (84)22.前向星 (87)23.矩阵 (92)24.并查集 (94)25. SORT (95)26. STL (97)27. LCA (HDU 2874) (101)28. 01背包 (104)29. 状态压缩代码: (107)30. 快速幂 (111)31.矩阵快速幂 (113)32.GCD & LCM (116)11834. /** 大数(高精度)求幂**/12335. /** 大数除法与求余**/ (127)36. /** 大数阶乘**/ (131)37. /** 大数乘法**/ (133)38. /** 大数累加**/ (136)1.最小生成树要连通n个城市需要n-1条边线路。
可以把边上的权值解释为线路的造价。
则最小生成树表示使其造价最小的生成树。
prim算法(矩阵形式):#define inf 0x3f3f3f3fint prim(int n,int sta)//n表示有n个顶点,sta表从sta这个顶点出发生成最小生成树{int mark[M],dis[M];int i,sum = 0; //sum是总的最小生成树边权值for (i = 0;i < n;i ++) //初始化dis[i] 表从顶点sta到点i的权值 {dis[i] = mat[sta][i];mark[i] = 0;}mark[sta] = 1; //sta 这个顶点加入最小生成树中 for (i = 1;i < n;i ++) //循环n-1次,每次找出一条最小权值的边 n个点的图{ //只有n-1条边int min = inf; //inf 表无穷大for (j = 0;j < n;j ++)//找出当前未在最小生成树中边权最小的顶点if (!mark[j] && dis[j] < min)min = dis[j],flag = j;mark[flag] = 1; //把该顶点加入最小生成树中sum += dis[flag]; //sum加上其边权值for (j = 0;j < n;j ++) //以falg为起点更新到各点是最小权值if (dis[j] > mat[flag][j])dis[j] = mat[flag][j];}return sum; //返回边权总和}prim算法(边表形式):struct Edge//frm为起点,to为终点,w为边权,nxt指向下一个顶点{// int frm;int to,w,nxt;}edge[M];int vis[M],head[M],dis[M];void addedge (int cu,int cv,int cw)//生成边的函数{//edge[e].frm = cu;edge[e].to = cv;edge[e].w = cw;edge[e].nxt = head[cu];head[cu] = e ++;//edge[e].frm = cv;edge[e].to = cu;edge[e].w = cw;edge[e].nxt = head[cv];head[cv] = e ++;}int prim(int n,int sta) //n为顶点数量,sta为起点{int sum = 0;memset(dis,0x3f,sizeof(dis));memset(vis,0,sizeof(vis));for (i = head[sta];i != -1;i = edge[i].nxt)//遍历与sta点相连的所有顶点 {int v = edge[i].to;dis[v] = edge[i].w;}vis[sta] = 1; //加入到最小生成树中int m = n - 1; //只生成n-1条边,所以循环n-1次while (m --){int min = inf;for (i = 0;i < n;i ++)//找出当前边权最小的边if (!vis[i]&&dis[i] < min)flag = i,min = dis[i];sum += dis[flag];vis[flag] = 1;//加入到最小生成树中for (i = head[flag];i != -1;i = edge[i].nxt) //更新与flag顶点相连的点的dis{int v = edge[i].to;if (edge[i].w < dis[v])dis[v] = edge[i].w;}}return sum; //返回边权总和}int main (){e = 0; //记得初始化memset (head,-1,sizeof(head));scanf ("%d %d %d",&a,&b,&w);addedge(a,b,w);..........prim(n,sta);return 0;}Kruskal算法:struct Edge{int v1,v2,w;}edge[M],tree[M]; //w为v1顶点到v2顶点的边权/ *int Find (int parent[],int u)//第1种写法{int tmp = u;while (paren[tmp] != -1)tmp = parent[tmp];return tmp;}*/int Find (int u) //第2种写法{if (u != parent[u])parent[u] = Find(paren[u]);return parent[u];}bool cmp (Edge a,Edge b){return a.w < b.w;}int Kruskal()//parent[]表示集合{int parent[M];int i,j,sum,vf1,vf2;sort(edge,edge+E,cmp);// memset (parent,-1,sizeof(parent));//对应第1种并查集的初始化for (i = 0;i < n;i ++) //对应第2种并查集的初始化parent[i] = i;sum = i = j = 0;while (i < E && j < N - 1)//生成的边数为N-1{vf1 = Find(parent,edge[i].v1); //找这两个点的祖先vf2 = Find(parent,edge[i].v2);if (vf1 != vf2) //若两个点的祖先不同,说明不在同一集合{parent[vf2] = vf1;//把vf2点加到vf1点的集合中tree[j++] = edge[i];//把边加到tree[]数组中,这句题目没要求可忽略之sum += edge[i].w; //sum 加上其边权}i ++;}return sum;}最小生成树 -- Kruskal算法:运用数组存点与边的权值#include <stdio.h>#include <stdlib.h>#include <algorithm>#define N 150using namespace std;int m,n,u[N],v[N],w[N],p[N],r[N];int cmp(const int i,const int j) {return w[i]<w[j];} int find(int x) {return p[x]==x?x:p[x]=find(p[x]);}int kruskal(){int cou=0,x,y,i,ans=0;for(i=0;i<n;i++) p[i]=i;for(i=0;i<m;i++) r[i]=i;sort(r,r+m,cmp);for(i=0;i<m;i++){int e=r[i];x=find(u[e]);y=find(v[e]);if(x!=y) {ans += w[e];p[x]=y;cou++;}}if(cou<n-1) ans=0;return ans;}int main(){int i,ans;while(scanf("%d%d",&m,&n)!=EOF&&m){for(i=0;i<m;i++){scanf("%d%d%d",&u[i],&v[i],&w[i]);}ans=kruskal();if(ans) printf("%d\n",ans);else printf("?\n",ans);}return 0;}2.最短路算法①DIJKC++代码1.#define inf 0x3fffffff2.#define M 1053.4.int dist[M], map[M][M], n;5.bool mark[M];6.7.void init ()8.{9. int i, j;10. for (i = 1; i <= n; i++) //i==j的时候也可以初始化为0,只是有时候不合适11. for (j = 1; j <= n; j++)12. map[i][j] = inf;13.}14.15.void dijk (int u)16.{17. int i, j, mins, v;18. for (i = 1; i <= n; i++)19. {20. dist[i] = map[u][i];21. mark[i] = false;22. }23. mark[u] = true;24. dist[u] = 0; //既然上面的map当i==j时不是0,就要这句25. while (1)26. {27. mins = inf;28. for (j = 1; j <= n; j++)29. if (!mark[j] && dist[j] < mins)30. mins = dist[j], v = j;31. if (mins == inf)32. break;33. mark[v] = true;34. for (j = 1; j <= n; j++)35. if (!mark[j] && dist[v] + map[v][j] < dist[j])36. dist[j] = dist[v] + map[v][j];37. }38.}②Floyd1.#define inf 0x3fffffff //注意,太大会溢出2.#define M //最大点数3.int n, dist[M][M]; //n:实际点数4.5.void init () //有时候需要初始化6.{7. int i, j;8. for (i = 1; i <= n; i++)9. for (j = i + 1; j <= n; j++)10. dist[i][j] = dist[j][i] = inf;11.}12.13.void floyd ()14.{15. int i, j, k;16. for (k = 1; k <= n; k++)17. for (i = 1; i <= n; i++)18. for (j = 1; j <= n; j++) //有的题目会溢出就要自己变通了19. if (dist[i][k] + dist[k][j] < dist[i][j])20. dist[i][j] = dist[i][k] + dist[k][j];21.}③vector后插的SPFAC++代码1.#define inf 0x3fffffff2.#define M 105 //最大点数3.struct son{4. int v, w;5.};6.vector<son> g[M];7.bool inq[M]; //入队列标记8.int dist[M], n; //n:实际点数9.10.void init ()11.{12. for (int i = 1; i <= n; i++)13. g[i].clear();14.}15.16.void spfa (int u)17.{18. int i, v, w;19. for (i = 1; i <= n; i++)20. {21. dist[i] = inf;22. inq[i] = false;23. }24. queue<int> q;25. q.push (u);26. inq[u] = true;27. dist[u] = 0;28. while (!q.empty())29. {30. u = q.front();31. q.pop();32. inq[u] = false;33. for (i = 0; i < g[u].size(); i++)34. {35. v = g[u][i].v;36. w = g[u][i].w;37. if (dist[u] + w < dist[v])38. {39. dist[v] = dist[u] + w;40. if (!inq[v])41. {42. q.push (v);43. inq[v] = true;44. }45. }46. }47. }48.}④模拟前插的SPFA(多数情况下比③快,数据较为复杂就会看出来) C++代码1.#define inf 0x3fffffff2.#define M 1005 //最大点数3.4.struct edge{5. int v, w, next;6.}e[10005]; //估计好有多少条边7.8.int pre[M], cnt, dist[M], n;9.bool inq[M];10.//注意初始化11.void init ()12.{13. cnt = 0;14. memset (pre, -1, sizeof(pre));15.}16.//注意双向加边17.void addedge (int u, int v, int w) //加边函数,慢慢模拟就会明白的18.{19. e[cnt].v = v;20. e[cnt].w = w;21. e[cnt].next = pre[u]; //接替已有边22. pre[u] = cnt++; //自己前插成为u 派生的第一条边23.}24.25.void spfa (int u)26.{27. int v, w, i;28. for (i = 1; i <= n; i++) //对于从1到n的编号29. dist[i] = inf, inq[i] = false;30. dist[u] = 0;31. queue<int> q;32. q.push (u);33. inq[u] = true;34. while (!q.empty())35. {36. u = q.front();37. q.pop();38. inq[u] = false;39. for (i = pre[u]; i != -1; i = e[i].next)40. {41. w = e[i].w;42. v = e[i].v;43. if (dist[u] + w < dist[v])44. {45. dist[v] = dist[u] + w;46. if (!inq[v])47. {48. q.push (v);49. inq[v] = true;50. }51. }52. }53. }54.}3.素数打表#include<iostream>#include<cstring>#include<cmath>#define MAXN 5000using namespace std;int prime[MAXN];void print_prime(){int n = (int) sqrt(MAXN); for(int i = 2; i < n; i++) {if( !prime[i] ){for(int j = i*i; j < MAXN; j += i)prime[j] = 1;prime[++prime[0]] = i;}}for(int i = n; i < MAXN; i++)if(!prime[i])prime[++prime[0]] = i;}int main(){FILE *out;out = fopen("out.txt","w");print_prime();for(int i = 1; i <= prime[0]; i++)fprintf(out, "%d,", prime[i]); return 0;}4.最大匹配#include<stdio.h>#include<string.h>#define MAX 505bool used[MAX];bool match[MAX][MAX];int boys[MAX],girls[MAX];int k, n;void init(){memset(girls, -1, sizeof(girls));memset(boys, -1, sizeof(boys));memset(match, false, sizeof(match)); }bool can(int t){int i;for(i = 0; i < n; i ++){if(!used[i] && match[t][i]){u sed[i] = true;if(girls[i] = = -1 || can(girls[i])) {boys[t] = i;girls[i] = t;return true;}}}return false;}int main(){int tmpa, tmpb, i, res, t;while(scanf("%d", &n) != EOF){init();for(i = 0; i < n; ++i){scanf("%d: (%d)", &tmpa, &k);while(k- -){scanf("%d", &tmpb);match[tmpa][tmpb] = true;res = 0;for(i = 0; i < n; i++){if(boys[i] == -1){memset(used, false, sizeof(used));if(can(i)) res ++;}}}}printf("%d\n", n - res/2);}return 0;}5.线段树(敌兵布阵)#include<stdio.h>#include<string.h>int a[50005],n;void update(int x,int c){int i;for(i=x;i<=n;i+=(i&(-i)))a[i]+=c;}int s(int x){int i;int sum=0;for(i=x;i>0;i-=(i&-i)){sum+=a[i];} return sum;}int main(){int t,i,c,b=1;int z,y;char sh[15];scanf("%d",&t);while(t--){memset(a,0,sizeof(a));scanf("%d",&n);for(i=1;i<=n;i++){scanf("%d",&c);update(i,c);}printf("Case %d:\n",b++); while(~scanf("%s",sh)){if(sh[0]=='E')break;scanf("%d%d",&z,&y);if(sh[0]=='A')update(z,y);else if(sh[0]=='S')update(z,-y);else printf("%d\n",s(y)-s(z-1));}}return 0;}6.线段树(逆序树)#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;int a[500005],n;int b[500005];int d[500005];void update(int x,int c){ int i;for(i=x;i<=n;i+=(i&(-i))) a[i]+=c;}int s(int x){int i;int sum=0;for(i=x;i>0;i-=(i&-i)){sum+=a[i];}return sum;}int main(){int i,j;int ans;while(scanf("%d",&n)!=EOF,n) {memset(d,0,sizeof(d));for(i=1;i<=n;i++){scanf("%d",&b[i]);b[i]=b[i]+1;d[i]=b[i];}sort(b+1,b+n+1);memset(a,0,sizeof(a));ans=0;for(i=1;i<=n;i++){update(d[i],1);printf("%d ",s(d[i]));ans+=i-s(d[i]);printf("%d ",ans);}printf("%d\n",ans);}return 0;}7.树形dp#include<stdio.h>#include<iostream>using namespace std;struct Tree{intfather,child,brother,with_max,without_max ;int MAX(){returnwith_max>=without_max?with_max:without_ma x;}void init(){father=child=brother=without_max=0;}}tree[6001];void dfs(int id)int child;child=tree[id].child;while(child){dfs(child);tree[id].with_max+=tree[child].without_ max;tree[id].without_max+=tree[child].MAX() ;child=tree[child].brother;}}int main(){int n,i;while(scanf("%d",&n)!=EOF){for(i=1;i<=n;i++){scanf("%d",&tree[i].with_max);tree[i].init();}int a,b;while(scanf("%d%d",&a,&b),a||b){tree[a].father=b;tree[a].brother=tree[b].child;tree[b].child=a;}for(i=1;i<n;i++)if(!tree[i].father){dfs(i);printf("%d\n",tree[i].MAX());break;}}return 0;}8.树状数组(段跟新)1.#include <stdio.h>2.#include <string.h>3.const int MAXN=110000;4.int n,c[MAXN];5.int lowbit(int x)6.//计算2^k7.{8. x=x&-x;9. return x;10.}11.void update(int num,int val)12.//向下查询,num是要更新的子节点,val是要修改的值13.{14. while(num>0)15. {16. c[num]+=val;17. num-=lowbit(num);18. }19.}20.int getSum(int num)21.//向上统计每个区间被染色的次数22.{23. int sum=0;24. while(num<=n)25. {26. sum+=c[num];27. num+=lowbit(num);28. }29. return sum;30.}31.int main()32.{33. int a,b;34. while(scanf("%d",&n),n)35. {36. memset(c,0,sizeof(c));37. for(int i=0;i<n;i++)38. {39. scanf("%d%d",&a,&b);40. //将b以下区间+141. update(b,1);42. //将a以下区间-143. update(a-1,-1);44. }45. for(int j=1;j<n;j++)46. {47. printf("%d ",getSum(j));48. }49. printf("%d\n",getSum(n));50. }51. return 0;52.}9.Kmp模板#include<stdio.h>#include<string.h>#include<iostream>using namespace std;#define N 1000010int len,len1,next[N];int str[N],str1[N];void get_next(){int i=0,j=-1;next[0]=-1;while(i<=len1){if(j==-1||str1[i]==str1[j]){i++;j++;next[i]=j;}elsej=next[j];}}int kmp(){int i=0,j=0;{if(j==-1||str[i]==str1[j]) {i++;j++;}else{j=next[j];}}if(j>=len1){return i-len1+1;}return 0;}int main(){int i,t,flag;scanf("%d",&t);while(t--){memset(str,0,sizeof(str));memset(str1,0,sizeof(str1));memset(next,0,sizeof(next));scanf("%d%d",&len,&len1);for(i=0;i<len;i++)scanf("%d",&str[i]);for(i=0;i<len1;i++)scanf("%d",&str1[i]);get_next();flag=kmp();// p rintf("%d\n",flag);if(flag==0)printf("-1\n");elseprintf("%d\n",flag);}return 0; }计算出字符串 str2 中含有的 str1 的个数#include<stdio.h>#include<string.h>int next[10005];char str1[10005], str2[1000005];int n, m, ans;void getNext(char *p, int *next){int j, k;next[0] = -1;j = 0;k = -1;while(j < m){if(k == -1 || p[j] == p[k]) //匹配的情况下,p[j]==p[k]{j++;k++;next[j] = k;//printf("%d **%d **%d\n",j, k, next[j]);}else//p[j]!=p[k]k = next[k];}}int KMPMatch(char *s, char *p){int i = 0, j = 0;getNext(p, next);/*for(i = 1; i < m; i++) printf("%d ", next[i]);i = 0; printf("\n");*/while(i < n){if(j == -1 || s[i] == p[j]){i++;j++;}else{j = next[j]; //消除了指针i的回溯printf("i = %d, j = %d\n", i, j), }if(j == m )ans++;//return i - m + 1;}return ans;}int main(){int T;scanf("%d",&T);while(T--){memset(str1, '\0', sizeof(str1));memset(str2, '\0', sizeof(str2));memset(next, 0, sizeof(next));ans = 0;scanf("%s", str1);scanf("%s", str2);n = strlen(str2);m = strlen(str1);printf("%d\n",KMPMatch(str2, str1));}return 0;}10.线段树(点跟新)#include<stdio.h>#include<string.h>#define N 200010struct Node{int l,r,max;}node[3*N];int score[N];int max(int a,int b){return a>b?a:b;}void Build(int left,int right,int index) {node[index].l=left;node[index].r=right;if(left==right)node[index].max=score[left];else{int mid;mid=(left+right)/2;Build(left,mid,index*2);Build(mid+1,right,index*2+1);node[index].max=max(node[2*index].max,n ode[2*index+1].max);}}void Update(int stu,int c,int index){node[index].max=max(c,node[index].max);if(node[index].l==node[index].r){return ;}if(stu<=node[2*index].r){Update(stu,c,index*2);}else{Update(stu,c,index*2+1);}}int query(int left,int right,int index) {if(left==node[index].l&&right==node[ind ex].r){return node[index].max;}if(right<=node[2*index].r){return query(left,right,index*2);}if(left>=node[2*index+1].l){return query(left,right,index*2+1);}intmid=(node[index].l+node[index].r)/2;returnmax(query(left,mid,2*index),query(mid+1,r ight,index*2+1));}int main(){int n,m;while(scanf("%d%d",&n,&m)!=EOF) {int i;for(i=1;i<=n;i++)scanf("%d",&score[i]);getchar();char c;int s,e;Build(1,n,1);for(i=0;i<m;i++){scanf("%c%d%d",&c,&s,&e);getchar();if(c=='U'){Update(s,e,1);}if(c=='Q'){printf("%d\n",query(s,e,1));}}}return 0;11.强连通# include<stdio.h># include<string.h># define N 1005# define M 2005struct node{int from,to,next;}edge1[M],edge2[M];intvisit1[N],visit2[N],head1[N],head2[N],T[N ];intin_degree[N],out_degree[N],tol1,tol2,Bcnt ,Tcnt;int val[N],v1[N],min,Belong[N];。
参加ACM比赛所需基础知识 参加ACM比赛计划建议 3个阶段
2. 平时扫扫zoj上的难题啦,别老做那些不用想的题.(中大acm的版主经常说我挑简单的来
做:-P )
3. 多参加网上的比赛,感受一下比赛的气氛,评估自己的实力.
4. 一道题不要过了就算,问一下人,有更好的算法也打一下。
参加ACM比赛所需基础知识 参加ACM比赛计划建议 3个阶段2008-08-07 20:30参加ACM比赛一般要做到50行以内的程序不用调试、100行以内的二分钟内调试成功.acm主要是考算法的,主要时间是花在思考算法上,不是花在写程序与debug上。
第一阶段:
练经典常用算法,下面的每个算法给我打上十到二十遍,同时自己精简代码,因为太常用,所以要练到写时不用想,10-15分钟内打完,甚至关掉显示器都可以把程序打
5. 熟悉动态规划的各个典型:LCS、最长递增子串、三角剖分、记忆化dp
6.博弈类算法。博弈树,二进制法等。
7.最大团,最大独立集。
8.判断点在多边形内。
9. 差分约束系统.
10. 双向广度搜索、A*算法,最小耗散优先.
第三阶段:
前两个阶段是打基础,第三阶段是锻炼在比赛中可以快速建立模型、想新算法。这就要平时多做做综合的题型了。
5. 做过的题要记好 :-)
�
7.数学上的有:辗转相除(两行内),线段交点、多角形面积公式.
8. 调用系统的qsort, 技巧很多,慢慢掌握.
9. 任意进制间的转换
第二阶段:
练习复杂一点,但也较常用的算法。
如:
1. 二分图匹配(匈牙利),最小路径覆盖
2. 网络流,最小费用流。
3. 线段树.
最小生成树题目
最小生成树题目 最小生成树是图论中的一个重要概念,被广泛应用于路由算法、网络设计、电力传输等领域。
最小生成树问题可以简单描述为:给定一个连通图,选择一些边使得图中所有节点都能够连接,并且总边权之和最小。
最小生成树题目是在解决最小生成树问题时所遇到的具体情境。
以下通过分析两个不同的最小生成树题目,来理解最小生成树算法的应用。
题目1:某城市的道路规划 假设一个城市有多个地区,每个地区之间需要建立道路来连接。
已知每条道路的长度,在保证每个地区都能连通的情况下,设计一个道路规划方案,使得总道路长度最小。
解题思路: 1、首先,根据题目中给出的道路长度,建立一个无向带权图。
其中,每个地区对应图的节点,道路对应图的边,道路长度对应边的权值。
2、通过使用Kruskal或Prim算法,从这个带权图中构建最小生成树,即选取一些道路使得所有地区连通,并且这些道路的权值之和最小。
3、最小生成树即为最优的道路规划方案,输出最小生成树的边集合即可。
题目2:电力传输网络设计 某地区有多个居民点,需要建立电力传输网络来确保每个居民点都能接收到电力供应。
已知每个居民点之间建立电力线路的成本,在保证每个居民点都能接收到电力供应的情况下,设计一个电力传输网络,使得总成本最小。
解题思路: 1、根据题目给出的电力线路成本,建立一个带权完全图。
其中,每个居民点对应图的节点,电力线路对应图的边,电力线路成本对应边的权值。
2、通过使用Kruskal或Prim算法,从这个带权图中构建最小生成树,即选取一些电力线路使得所有居民点都能接收到电力供应,并且这些电力线路的成本之和最小。
3、最小生成树即为最优的电力传输网络设计方案,输出最小生成树的边集合即可。
最小生成树问题是一个经典的优化问题,通过构建最小生成树,我们可以找到图中连接所有节点的最优边集合。
在实际应用中,最小生成树算法可以帮助我们进行有效的资源分配、网络规划等决策。
总体来说,最小生成树题目涉及到图的建模和优化算法的运用。
ACM在线训练方法09
poj3273,poj3258,poj1905,poj3122 poj2976
poj3150,poj3422,poj3070 poj3301
poj2031,poj1039
poj1408,poj1584 poj2187,poj1113 poj1151 poj1765,poj1177,poj1151,poj3277,poj228 0,poj3004 poj3130,poj3335 poj1819,poj1066,poj2043,poj3227,poj216 5,poj3429 poj3384,poj2540 poj2966 poj2079 poj3318,poj2454 poj1870,poj3296,poj3286,poj1095 poj3317,poj1085 poj3109,poj1478,poj1462,poj2729,poj204 8,poj3336,poj3315,poj2148,poj1263
(1)串
poj1753,poj2965 poj1328,poj2109,poj2586 很多,不专门列题 很多,不专门列题 poj3295 poj1068,poj2632,poj1573,poj2993,poj299 6 poj3096,poj3007 poj3393,poj1472,poj3371,poj1027,poj270 6 poj2525,poj1684,poj1421,poj1048,poj205 0,poj3306 poj3434
1.加法原理和乘法原理. 2.排列组合. 3.递推关系.
1.容斥原理. 2.抽屉原理. 3.置换群与 Polya 定理 4.递推关系和母函数. 1.MoBius 反演 2.偏序关系理论
1.素数与整除问题 2.进制位. 3.同余模运算.
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
POJ 1251 Jungle Roads小感:第一次做图论题目,而且一次就AC了,挺兴奋的,呵呵。
当然,还有很多不是非常明白的地方,对算法很细节的执行还没有非常明白~还有就是,Prim算法的过程好像是选出一个点后,找该点与剩下的点的权值的最小值,而不是所有在U中的点与V-U中的点的最小值。
开始时觉得应该是所有U中的与V-U中最小的值的~还得再看看~这里用了Prim求最小生成树,图是用邻接矩阵表示的,算法复杂度为O(n2),其实看书上的例子,是用邻接表实现的,而且用到了最小堆求权值最小的边,最后的复杂度为O(elog2e),e为边数。
总的过程是从V-U中选出到U中权值最小的点,并加到U,之后更新lowcost,被更新的lowcost只是在V-U中的。
还有就是关于scanf,开始时用,可是在输入的时候有问题,不知道怎么回事~看别人也都说scanf会RE等的,于是用了cin,结果0MS就可以过了~还有,也看到有人说用了26时,RE 了,所以我就改成27了,也不知道26到底会不会错~#include <iostream>#define MAX 27#define MAXCOST 101using namespace std;int vertices[MAX],lowcost[MAX],minv,totalcost;int edges[MAX][MAX];bool visited[MAX];int prim(int vertex_num,int v){totalcost=0;visited[v]=true;for(int i=0;i<vertex_num;i++)lowcost[i]=edges[v][i];for(int i=1;i<vertex_num;i++){minv=MAXCOST;//for选出从U中顶点到V-U中顶点的最小权值,每次从第一个开始//这样如果碰到边界顶点时,可以回到另外的点for(int j=0;j<vertex_num;j++)if(visited[j]==false&&lowcost[j]<minv)//lowcost[j]是从顶点v到顶点j的权值{minv=lowcost[j];v=j;}visited[v]=true;totalcost+=minv;for(int j=0;j<vertex_num;j++)if(visited[j]==false&&edges[v][j]<lowcost[j])lowcost[j]=edges[v][j];}return totalcost;}int main(){int num,degree,cost;char vertex;while(cin>>num){if(num==0) break;for(int i=0;i<num;i++) vertices[i]=i;for(int i=0;i<num;i++)for(int j=0;j<num;j++)edges[i][j]=MAXCOST;for(int i=0;i<num;i++) visited[i]=false;for(int i=0;i<num-1;i++){cin>>vertex>>degree;for(int j=0;j<degree;j++){cin>>vertex>>cost;edges[i][vertex-'A']=cost;edges[vertex-'A'][i]=cost;}}cout<<prim(num,0)<<endl;}return 0;}POJ 1258 Agri-NetSlyar:简单介绍一下题意。
农民要建立互联网络,目的使村庄里所有的农民连上网,并且总费用最小。
多组数据,每组数据给出一个n,然后给出n * n大小的无向图的邻接矩阵表示,值表示边权。
要求输出最小生成树的权值和。
这次用朴素的Prim写。
Prim详见/blog/prim-simplicity-c.html#include <stdio.h>#include <stdlib.h>#define MAX 101#define MAXCOST 0x7fffffffint graph[MAX][MAX];int Prim(int graph[][MAX], int n){int lowcost[MAX];int mst[MAX];int i, j, min, minid, sum = 0;for (i = 2; i <= n; i++){lowcost[i] = graph[1][i];mst[i] = 1;}mst[1] = 0;for (i = 2; i <= n; i++){min = MAXCOST;minid = 0;for (j = 2; j <= n; j++){if (lowcost[j] < min && lowcost[j] != 0){min = lowcost[j];minid = j;}}sum += min;lowcost[minid] = 0;for (j = 2; j <= n; j++){if (graph[minid][j] < lowcost[j]){lowcost[j] = graph[minid][j];mst[j] = minid;}}}return sum;}int main(){int i, j, k, m, n;int cost;while (scanf("%d", &n) != EOF){for (i = 1; i <= n; i++){for (j = 1; j <= n; j++){graph[i][j] = MAXCOST;}}for (i = 1; i <= n; i++){for (j = 1; j <= n; j++){scanf("%d", &graph[i][j]);}}cost = Prim(graph, n);printf("%d\n", cost);}return 0;}POJ 1789 Truck History 最小生成树题意:n的卡车,每辆车拥有唯一的7位车牌号。
两两之间车牌号的有几位不相同就表示有多大的差别,即两代之间的距离有多远,求联通所有带的最短距离和。
题解:理解题意后不难发现就是一个简单的最小生成树。
和原来低效的prim写法不同的是用mdis [i]来更新到每个点的最小边,这样可以省去一层循环。
#include <stdio.h>#include <stdlib.h>#include <string.h>#define MAX 99999int n,dis[2010][2010],in[2010],dif,ans,mdis[2010];char a[2010][7];void prim(){int i,j,k,min,v;ans = 0;memset(in,0,sizeof(in));in[1] =1 ;for (i=1;i<=n;i++)mdis[i] = dis[1][i];for (i=1;i<n;i++){min = MAX;for (j=1;j<=n;j++)if (!in[j] && mdis[j]<min){v = j;min = mdis[j];}ans += min;if (!in[v]) in[v] = 1;for (k=1;k<=n;k++)if (!in[k] && mdis[k]>dis[v][k])mdis[k] = dis[v][k];}}int main()int i,j,k;while (scanf("%d", &n),n){getchar();for (i=1;i<=n;i++){gets(a[i]);for (j=1;j<i;j++){dif = 0;for (k=0;k<7;k++)if (a[i][k]!=a[j][k])dif++;dis[i][j] = dis[j][i] = dif;}dis[i][i] = MAX;}prim();printf("The highest possible quality is 1/%d.\n", ans);}}POJ 2485 Highways一看就知是最小生成树,开始的时候用Kruskal出问题,改用Prim过了POJ,现在回过头来用Kruskal算法做,也好,又过了。
发出来,或许也会有像我这样曾经被这个问题搞郁闷的朋友。
问题抽象一个下就是求Kruskal算法中最后加入的那条边的长度,同样是Prim算法中加入的边的最长的那条。
prim解法:#include <iostream>using namespace std;int highway[501][501];int lowcost[501], n;int prim(){for(int i = 1; i <= n; i++)lowcost[i] = highway[1][i];int ans = 0, nextVex, minEdge;for(int i = 1; i < n; i++){minEdge = 999999999;nextVex = 1;for(int j = 1; j <= n; j++){if((lowcost[j] < minEdge) && (lowcost[j] > 0)){minEdge = lowcost[j];nextVex = j;}}if(minEdge > ans)ans = minEdge;lowcost[nextVex] = 0;for(int j = 1; j <= n; j++){if(lowcost[j] > highway[nextVex][j]){lowcost[j] = highway[nextVex][j];}}}return ans;}int main(){int tcase;scanf("%d", &tcase);while(tcase--){scanf("%d", &n);for(int i = 1; i <= n; i++)for(int j = 1; j <= n; j++)scanf("%d", &highway[i][j]);printf("%d\n", prim());}return 0;}。