本周因为刚恢复线下上课,又有周六的阶段测试,导致时间紧缩,所以看的题数量不多。不过,看的博客和题解应该有20余篇(更多时间在耗费在钻研一题多解方面了),主要还是深搜方面的问题,具体涉及两部分:N皇后问题和01背包问题;不过本周倒是没参加比赛,但在参加面试的时候听同学和学长们说了点东西,也觉得有点收获。同时,自己也有观摩优秀同学的博客,也受益良多,最后就借着这个机会梳理一下自己的心路历程吧。
目录
1.N皇后问题
因为上周意识到自己在回溯方面薄弱,这周就主要找了点相关的资料和题目。恰好,N皇后就是一个非常典型的回溯类题目,所以就谈一下自己在这类题上的思路。
[USACO1.5]八皇后 Checker Challenge - 洛谷 |
N皇后的思路其实很明确,在一个矩阵中,搜索出行,列,对角线都只有一个的放置方式。不过需要注意的点也很明确,就是有的放置方式会无法产生合法的结果,一定要恰当的剪枝,通过回溯得到合理性的答案。看似这个题有行,列,两个对角线四个需要考虑的条件,但其实我们可以取巧,减少判定条件的个数,比如,行数作为循环的次数本身就已经确保不会重复了,接着我们只要保证每列和每个对角线只有一个就好。
在这里就有很多奇妙的思路了,比如,直接的进行判断标记:
for(int j=1;j<=n;j++)
{
if((!b[j])&&(!c[i+j])&&(!d[i-j+n]))
{
a[i]=j;//输入这是第几列的
b[j]=1;//宣布占领纵列
c[i+j]=1;
d[i-j+n]=1;
//宣布占领两条对角线
digui(i+1);//递归加深
b[j]=0;
c[i+j]=0;
d[i-j+n]=0;
//剪枝,进行回溯 }
}
再比如,我在一个博主的题解里得到的思路:寻找到对角线的规律,因为一个点在对角线上移动时它行移动的步数和列移动的步数是相等的,只要保证新放置的点与上一个移动的点不符合这个条件,就代表了他们不是一条对角线的,即满足条件,所以可以直接将其作为新条件判断:
bool check(int r,int c)
{
for(int i=0;i<r;i++)
if((col[i]==c)||(abs(col[i]-c)==abs(i-r)))return false;
return true;
}
还有,就是我个人的思路,借助当初连通块的想法,判断新放置的点与对角线的点是否有联系,有则剪枝回溯,无则放置(不过在自己打代码尝试之后发现费力还易超时,就换成了其他两个思路)。不过,问题的核心就是标记,回溯,和搜索,所以,某种程度来说,我的思路其实就是第二个思路的半成品。
其实,在这个问题里,我还看到了一些新颖的解题法,比如dancing links或者补充了二进制优化的解法等等,但殊途同归,最后平心而论,DFS确实是最简单的方法。
2.01背包问题
这个其实上课就讲了一道例题,但当时我没听明白,后来下来后自己回看和搜题解才有了想法。
其实这道题主要的点在于,每一件物品都有拿与不拿两种选择,所以在递归时,要保证两种选择不遗漏,然后选择最优解继续递归,其中,可以适时判断是否要优化进行剪枝。
#include<bits/stdc++.h>
#include<iostream>
using namespace std;
#define max=1000;
int v[max],w[max];
int n,all,bag[max],bag;
//v[],w[]装每个商品的信息,n是个数,all是总重(或者剩余的总重)
//bag指现在的重量
int bfs(int j,int now_v,int now_w)
{
if(j==n)
if(now_v>bag1)
bag1=now_v;
int temp=0;
for(int i=j;i<n;i++)
temp+=v[i];
if(now_w+temp<bag1)
return;
//剪枝,去掉已经不满足的结果
if(now_w>=weight[j])
dfs(j++,now_v+v[j],now_w+w[j]);
dfs(j++,now_v,now_w);
//重点,一定要判断两种情况都要判断
}
int main()
{
int i;
cin>>n>>all;
for(i==0;i<n;i++)
{cin>>v[i]>>w[i];bag[i]=0;}
bfs(0,0,all);
cout <<bag<<endl;
return 0;
}
自己打这个代码熬了有一个小时,其实和当时老师讲的思路基本一致,不过自己打了一遍确实思路更通透了。
3.个人感受体会
本周看了点其他同学的博客,在面试的时候也听到了一些同学的个人算法进度,我发现,目前我自己掌握的算法相对于其他同学来说是较少的,有的同学已经开始学习动态规划,很多已经掌握二叉树等等知识,但我对于绝大部分知识都是浅尝辄止,比如,对于STL,我只是达到了理解概念,会运用的地步,但个人实际在打代码时用的并不多,所以导致个人对这些知识并不熟练。
而且,听到有的同学cf分数达到700,800甚至近900的分数,我却只有500多,近600的分数,大概也就明白自己的差距了,我大概也能明白为什么我经常卡在一些问题上了,我当初选择学习ACM的初衷是为了提升自己的编程能力,并不指望一定能够参赛获奖,对于算法,我不觉得和别人比较进度或者分数是一个明智的想法,哪怕个人一步步的不断进步,这也算是一种提升了。
本周我新涉及的知识有链表,也明白了所谓分治的内容,对于动态数组的遍历以及扩容原因加深了了解,清楚了栈和队列的区别,就是为什么BFS使用队列而不使用栈等等细碎的小知识,比较大块的知识没来得及详细学习。
在使用方面,我大概就是清楚了在回溯时,究竟要如何改变已有的数据,如何进行数据更替,如何在搜索中使用STL的知识优化数据,方便输出(之前在理解STL的知识时大概设想过,现在具体明白如何应用,什么时候使用,如何使用挑选更有效率等等。),也通过几道例题清楚了怎样的剪枝能够有效的缩短时间提高效率,如何去挑选要剪的枝条,如何构造一个不影响原本搜索的剪枝等等。
也许起步慢是我的缺陷,但我想,一味的去于别人比较不是真正进步的方法,只有正视自己,明确自己的不足,将自己与他人的差距作为提升的途径,将过去蒙昧的自己作为比较对手,这才是真正进步的方法。
这一周先到这,下周要更努力的利用时间,去弥补自己的不足了。