[置顶] 洛谷N句话题解QWQ

皎月半洒花

2018-04-02 11:27:56

Solution

首先我会援引一些大佬的代码,如果大佬们看到自己的代码被挂了不开心,可以私信我,我会致谢或者删除$qwq$ ## $ps$:至于一些$zz$题……我就不整了$qwq$ 诶,这并不是一句话题解$qwq$,本蒟蒻太蒟了……所以会长一些qnq不过肯定会越来越短的啦!! ___________ $P1002$过河卒[甩链接](https://www.luogu.org/problem/show?pid=P1002#sub) 诶这个题不是广搜?$emmmmm$好像方案数这个东西,搜起来很耗时,并且结果有可能会爆$int$…… 所以我们选择DP……这好像也不能算是DP……也就是个递推吧$qwq$, 即,每一格的状态总是可以通过上方、左方两个状态转移过来。 啊,显然方案总数也是可以这样乱搞的$qwq$ 但是据说好像可以滚去一维的空间……今天上午脑子昏沉,看不懂,以后再说吧$qwq$ ---------------------------------------- $p1004$ 方格取数[甩链接](https://www.luogu.org/problem/show?pid=P1004#sub) 诶这个题不就是传纸条吗$qwq$ 据说可以状压一下下,但本蒟蒻不会呀! 既然是$DP$,那就考虑状态转移,只能从上下左右四个相邻的$classmates$传导过来$qwq$,那么就应该是二维$DP$,但由于有两个人,所以根据乘法原理,总共会有四维的状态总数,所以是四维$DP$. 哈,没那么简单,别忘了不能重复走这个问题$qwq$,其实只需要最后减去就好惹$QVQ$ $Such$ $as$ ```cpp f[i][j][k][l]=my_max(f[i][j-1][k-1][l],f[i-1][j][k][l-1],f[i][j-1][k][l-1],f[i-1][j][k-1][l])+a[i][j]+a[k][l]; if(i==k&&j==l)f[i][j][k][l]-=a[i][j]; ``` ________________________ $p1006$传纸条[link](https://www.luogu.org/problem/show?pid=P1006) 其实吧,这和上一个几乎是一个题……但其实是可优化的! 因为我们可以换一种方法来看待状态:我们可以将两个纸条看作同时传递,那么每一次移动的单位都是一定的,那么就可以有在上一个题中的$i+j=k+l=nowstep$, 所以我们可以通过枚举步数、分别枚举两个点的一个坐标,然后用$nowstep$推出另外一个坐标即可$qwq$. $Such$ $as$ ```cpp for (int k=1;k<=n+m-1;k++) for (int i=1;i<=n;i++) for (int j=1;j<=n;j++){ if (k-i<0 || k-j<0)continue; f[k][i][j] = my_max(f[k-1][i][j],f[k-1][i-1][j-1],f[k-1][i][j-1],f[k-1][i-1][j]) + a[i][k-i+1] + a[j][k-j+1]; } ``` ___________________________________ $p1010$ 幂次方[link](https://www.luogu.org/problem/show?pid=P1010#sub) 这个题一看就需要我们迭代操作……所以考虑递归。 那么递归到底的时候就只会有两种情况:出现$2(0)$或$2$,那我们就对于仍可以分解的每部分,中间以$"+"$号相连,每部分自身迭代下去就好。 嗯……递归算法是个好东西……但是需要理解透彻$qwq$ ```cpp void dfs(int n) { bool check=1; while(n) { if(!check)cout<<"+"; int w=(int)log2(n); if(w==1)cout<<"2"; else if(w==0)cout<<"2(0)"; else { cout<<"2(";dfs(w);cout<<")"; } n-=pow(2,w); check=0; } } ``` ___________________ $p1011$ 车站[link](https://www.luogu.org/problem/show?pid=P1011#sub) 乱搞一下就行……这是一道考察综合素质的水题……但是我做了好长时间$qwq$ 还有做题的时候一定要集中精力,读懂题再做……读三遍…… over. ____________________ $p1012$ 拼数[link](https://www.luogu.org/problem/show?pid=P1012#sub) 按照相邻的两个串相连接之后的字典序大小来写$sort$的$cmp$函数即可。 唔,还是要仔细想啊!水题也不能太过划水…… ______________________ $p1014$ Cantor表[link](https://www.luogu.org/problem/show?pid=P1014#sub) 啊,也是无脑的找规律,这个就只能看$OIer$的个人素养了$qwq$…… 对于这种题而言……还是不要抄题解的为好(逃 _______________________ $p1017$ 进制转换[link](https://www.luogu.org/problem/show?pid=P1017#sub) 这道题当时卡了我好久qwq……大约$45min$……因为我实在搞不清楚这个负进制是怎么个操作$qnq$…… 不过其实也挺简单的……就是要明白进制的定义: 对于一个数$k$,和一个数列$a_t…a_3a_2a_1a_0$,则对于一个$k$进制下的数,总可以表示成 ## $\Sigma_{i=0}^{n}(a_ik^i)$, $a_i\in[0,|k|)$ 这个式子看起来好像很富有亲和力$qwq$ $~~~~~$ ]反向逃。 也就是说每个进制位上的数只能是非负数啦!但是我们发现在短除法的时候有可能会出现负数,这个时候由于对于每个$a_i$都只会有$|a_i|<|k|$,所以我们只需要在这一位加上一个$|k|$即可,但是这样我们就需要向高位借一个$1$啦$qwq$ $Such$ $as$ ```cpp int divide(int n,int b,char *c) { int i=0,k; while(n!=0) { k=n%b; n/=b; if(k<0){k-=b;n+=1;} if(k>9)c[i]=char(k-10+'A'); else c[i]=char(k+'0'); i++; } return i-1; } ``` 这是从$DJblooky$大佬的题解里刨过来的$qwq$ ,在这里向这位大佬表示敬意! ___________ $p1018$ 乘积最大 毒瘤啊$qwq$……居然要高精…… 先说不高精的,就是个$dp$,但是$dp$多种多样啊!在这里我选了一种很不错的$dp$方式,$by$ LOI_lxt大佬: 用$dp[k][i]$表示用了$k$个乘号,前$i$个数的乘积最大值,那么我们可以通过每次只枚举最后一个乘号来实现状态的推演,因为之前的$k-w(0<=w<k)$已经枚举过了$qwq$。 哦,高精我还不是很熟啊,需要练习一下$qwq$ ___________ $p1019$ 单词接龙[link](https://www.luogu.org/problem/show?pid=P1019) 这道题是真你萌毒瘤啊$qnq$,虽然就是很水的模拟而已 忽略上一句极富有素质的感慨$qwq$ 首先我们要知道一些坑点……不读题的伤你不懂…… $1.$一个单词可以用两遍…… $2.$在加长度的时候,减去的不是最大重复部分,而是最小重复部分 $3.$较为复杂的题往往需要良好的心态和码力,所以蒟蒻还需要练习哇! 嗯,我还是太菜了。 最后付个辣鸡源码(主要的函数),顺便以期日后能够写得更简练。 ```cpp inline int add_haha(string a,string b) { int minus=0,tot=0; int la=a.size(),lb=b.size(); for(int i=0;i<=min(la,lb);i++) { bool flag=1; for(int j=la-i;j<=la-1;j++) { if(a[j]!=b[tot])flag=0; tot++; } if(flag&&!minus)minus=tot; tot=0; } return lb-minus; } inline bool check(string a,string b) { int tot=0; int la=a.size(),lb=b.size(),lla=la,llb=lb; for(register int i=0;i<min(la,lb);i++) { if(a[i]!=b[i]) break; else { lla--; llb--; } } int flag=0,ff=0,use=1; if((!lla&&llb)||(!llb&&lla))return 0; for(register int i=0;i<min(la,lb);i++) { for(register int j=la-i;j<=la-1;j++) { if(a[j]!=b[tot++])ff=1; use=0; } if(!ff&&!use)flag++; ff=0; tot=0; use=1; } if(!flag)return 0; return 1; } ``` _____________ $p1020$ 导弹拦截[link](https://www.luogu.org/problem/show?pid=P1020) 这个题不就是个最长上升子序列的裸题吗$qwq$,$nlogn$方法要好好打(背)。 求出一个子序列之后再乱搞贪心就好啊$qwq$ 贪心的时候就只需要对于每个导弹,我们都选择一个最小拦截高度的、但是可以拦截当前导弹的系统,如果没有可以拦截当前导弹的系统,系统个数就$++$。 ```cpp ………………………… x=0; for(k=1;k<=n;k++) if(h[k]>=a[i]) if(x==0)x=k; else if(h[k]<h[x])x=k; if(x==0)n++,x=n; h[x]=a[i]; i++; ………………………… ``` ______________ $p1021$ 邮票面值设计[link](https://www.luogu.org/problem/show?pid=P1021#sub) 这个题我当时不会做……嗯……留下了并没多好的印象$qnq$ 这个题我们首先可以考虑用深搜搜出来所用的几个数,而至于$MAX$,我们可以考虑$DP$,而需要注意以下这些$Tips$: $1.$虽然题目中说明了最多用$N$张邮票,也就是可以少放,但是多放的情况肯定大于少放的情况,所以直接对$n,k$进行$dfs$即可。 $2.$ $DFS$中的一个常见技巧——为了去重,可以将整个$ans$序列看作单调递增序列,比较好搞$qwq$ $3.$ 对于找$MAX$,我们可以以f[i]表示拼$i$所用的最小数的个数,那么在记录之后,我们就可以对$1$~$a[now]\times{n}$(因为当前最多取$a[now]\times{n}$,所以上界为$a[now]\times{n}$),直到筛到个数$>n$为止。 嗯……辅助DP嵌套$DFS$还真是让本蒟蒻吃不消啊$qwq$ 嗯,贴关键部分(求$MAX$) ```cpp int up=a[now]*n; for(int i=1;i<=now;i++) for(int j=a[i];j<=up;j++) f[j]=min(f[j],f[j-a[i]]+1); for(int i=1;i<=up;i++) if(f[i]>n) return i-1; return up; ``` 我可是真菜$qnq$ _____________________________ $p1024$ 一元三次方程求解[link](https://www.luogu.org/problem/show?pid=P1024) 这个题的数据范围怎么给的这么暴力哇??把数据范围再扩大个$100$倍暴力不也跑得过吗 以下这个好像是一个不知道对不对的二分……希望有大佬能指出问题来$qwq$ ```cpp for(double i=-100;i<=100;i++) { l=i,r=i+1; if(pks(i)<=0.01&&pks(i)>=-0.01)ans[++tot]=i,continue; while(r-l>=0.01) { double mid=(l+r)/2; if(pks(mid)>0.01)r=mid; else if(<pks(mid)<-0.01)l=mid; else ans[++tot]=mid; } } ``` _______________________________ $p1030$ 求先序排列[link](https://www.luogu.org/problem/show?pid=P1030#sub) 整理这个题的目的就是因为我老是忘记树的遍历是怎么回事…… 中:左->根->右 先:根->左->右 后:左->右->根 那么在我们知道后序遍历之后,最后一个元素就是根节点;然后再在中序遍历中查询根节点,得到左右子树,再到后序遍历中查找两个子树的根节点,然后继续迭代即可。 $emmmm$好像这题水的很,不过我还要为我没有打好树的基础而埋单啊$qnq$ ```cpp void build_pre(string pre,string aft) { if(pre.size()) { char root_now=aft[aft.size()-1]; cout<<root_now; int root=pre.find(root_now); build_pre(pre.substr(0,root),aft.substr(0,root)); build_pre(pre.substr(root+1),aft.substr(root,pre.size()-root-1)); } } ``` 感谢 sunyufei 大佬提供的一个很简洁的方法,此处鞠躬致敬$qwq$ _____________________ $P1037$ 产生数[link](https://www.luogu.org/problemnew/show/P1037#sub) 为什么是广搜$qwq$……只要用乘法原理把所有的字母的变换方式乘起来即可。 诶,是要高精的哇$qnq$($unsigned$ $long$ $long$ $int$正面硬刚不过) 吼哇吼哇,就这么做完了。 ______________________ $p1042$ 乒乓球 不挂$link$了……这个水题就是考你怎么输入的…… __________________________________________________ $p1044$ 栈 就是个卡特兰数求第$n$项啊……在这里可以提供一种证明的思路:既然是跟入栈次序有关,就可以对于一个卡特兰数$Cut$(胡蒙的英文),可以如此求: ## $Cut(n)=\Sigma_{i=1}^{n-1}(Cut(i)+Cut(n-i)),Cut(1)=1$ 嗯……浅显易懂啊,我就不证了(逃 _________________________ $p1052$ 过河[link](https://www.luogu.org/problem/show?pid=P1052) 啊,这是道好题哇$qwq$. 首先很明确的是,青蛙每一步的状态总是由上一步的状态转移过来的,也就是说$DP$方程是没什么思维难度的: f[i]表示走到第$i$单位长度时踩到石头的最小数量,那么有: ### $f[i]=min$ { $f[i]-length+check[i]$},$s<=length<=t$ 然鹅并不可做,因为$l$实在是太大了,$O(n)$都会炸,所以考虑离散化。而离散化好像排序和取模都可以,在这里我选择了膜法$qwqqq$ ____________________________ $p1057$ 传球游戏[link](https://www.luogu.org/problemnew/show/P1057#sub) 这个题考虑$dp$,那么首先定义状态,用$f[i][j]$表示传回编号$i$,传了$j$次的最多不同方案数。那么状态之间的转移就是 有 ### $f[1][j]=f[n][j-1]+f[2][j-1]$ ### $f[i][j]=f[i+1][j+1]+f[i-1][j+1]$ 那么最终答案就存在$f[1][m]$里 而实质上,对于一道$DP$题目,我们要先去审清它是不是一道$DP$,就是通过找状态和状态之间是否可以转移得出结论的。那么之后就需要考虑如何转移,即相邻的两个状态该如何演变。$DP$的特点就是好理解,但是理解是被动,主动做的话就会十分的$embarrassing$ ~~嗯,其实就是因为我蒟.~~ ________ $p1063$ 能量项链[link](https://www.luogu.org/problemnew/show/P1063#sub) $emmmm$这应该可以算是一个很经典的区间$DP$,那由于我的某篇$zz$博客专门讲了,就挂个链吧。 [区间$DP$](https://www.luogu.org/blog/pks-LOVING/higher-level-dynamic-programming-jiao-gao-ji-di-dong-tai-gui-hua-ou-j) _______ $p1064$ 金明的预算方案[link](https://www.luogu.org/problemnew/show/P1064) 这个题疑似是有依赖的背包问题,那么其实对于普通的这种有依赖的背包问题(无嵌套),我们是都可以转化成有多种情况的普通背包。 比如在这个题目中,我们就可以简化成有五种情况的01背包。即: 对于一个主件而言 1、不买; 2、买自己,也买自己的左儿子; 3、买自己,也买自己的右儿子; 4、只买自己; 5、买自己,左儿子买,右儿子也买; 而附件是没有任何选择权的(只能被选择),所以不枚举附件。 ____________________ $p1075$ 质因数分解[link](https://www.luogu.org/problemnew/show/P1075#sub) 这道题其实没什么可整理的,就是个水题啊,但是让我至今也很记忆犹新的,是下面这份十分简练的$rqy$在初三上学期写出来的代码: ```cpp cin>>n; int i=2; while(n%i!=0)i++; cout<<n/i; ``` 我从来不肯承认有人真的比我强很多,所以我虽然很佩服$rqy$,但我也要把它当作一个争取超越的对象啊。 $"The$ $limit$ $of$ $your$ $mind$ $is$ $the$ $limit$ $of$ $your$ $world."$ ___________________ $p1080$ 国王游戏[link](https://www.luogu.org/problemnew/show/P1080#sub) 这个题本来不是很恶心,但是因为是个$NOIp$的题目,所以不加一个高精对不起这个题的毒瘤属性$qwq$,所以要加一个高精来毒瘤以下。 这个题的贪心方向应该很明显,就是按某种方式进行排序,然后使得最大的大臣收益最小。好的,那么$sort$需要一个$cmp$,那么现在的问题就是怎么写一个$cmp$。 很显然啊,排序的时候最好的方案应该是让在其他状态下收益高的大臣在前面,让受益小的大臣在后面。 而排序,在这里给出花姐姐自己的证明: 设正在进行$cmp$的两个大臣的左右手上写的为$l_1r_1,l_2r_2$在他俩之前总会有至少一个人,设其左右上的数字为$l_0r_0$。 那么对于两种不同的顺序,即1在2之前和2在1之前,有两种不同的收益: $1$在$2$前:$max(\frac{l_0l_1}{r_2},\frac{l_0}{r_1})$ $2$在$1$前:$max(\frac{l_0l_2}{r_1},\frac{l_0}{r_2})$ 那么对于这两个式子,我们设第一种情况的值要大于第二种情况,即让1在前面会更优,便可以得到: $max(\frac{l_0l_1}{r_2},\frac{l_0}{r_1})>max(\frac{l_0l_2}{r_1},\frac{l_0}{r_2})$ 因为都是正值,所以两边同乘r_2r_1可以有: $max(l_0l_1r_1,l_0r_2)>max(l_0r_1,l_0l_2r_2)$ 提取$l_0$可得: $max(l_1r_1,r_2)>max(r_1,l_2r_2)$ 而因为若式子左边有$r_2>l_1r_1$,那么就不可能有左边大于右边,因为显然$r_2<l_2r_2$,所以右边同理,可将原始式子化作: $l_1r_1>l_2r_2$ 这就是$cmp$ 至于高精乘,人家不会了啦$qwqqq$ ________________________ $p1087$ $FBI$树[link](https://www.luogu.org/problemnew/show/P1087#sub) 嗯,很棒啊,又是一道辣鸡橙题,然鹅当时我还是不会做$OTZ$ 他这个题很有良心的一点是让你后序遍历输出,那么很显然了哇,输出操作应该放在递归之后,也就是回溯的时候进行。那么在回溯的时候只需要在记录的左右区间内跑一遍就好。 好吧,当时不会的主要原因就是因为我对于树的遍历这部分掌握的太渣了$qnq$ __________________________ $p1088$ 火星人 [link](qhttps://www.luogu.org/problemnew/show/P1088#sub) 这道题就是一个$stl$的模板题……全排列直到$m--$变成0为止…… 那么在此,整理一下$next$_$permutation()$的用法,这个函数是一个$bool$型的,返回值的意义是是否还有下一个排列,与之相反的是$prev$_$permutation()$,那么对于这两个函数: $next$_$permutation()$ $bool$型 直接对原数组进行操作 所有排列的字典序是递增的 $prev$_$permutation()$ $bool$型 同上 $~~~~~~~~~~~~~~~~~~~~~~~~~$ 所有排列的字典序是递减的 $emmm$就这样吧 ___________________________ $p1115$ 最大子段和[link](https://www.luogu.org/problemnew/show/P1115) 这个地方给出一个$dp$做法。在求子段和的时候由于是由跨负数的,所以在我们的第一次循环之后,在第二次循环再进行一次反悔操作。当然同时也要保证最优性$QWQ$。那么就可以得到如下程序段: ```cpp for(int i=1;i<=n;i++) cin>>A[i]; dp[1]=A[1]; for(int i=2;i<=n;i++) dp[i]=max(dp[i-1]+A[i],A[i]); for(int i=2;i<=n;i++) dp[i]=max(dp[i-1],dp[i]); ``` 这就是如何求最大子段和的$DP$做法$qwq$