#include <iostream>
#include <cstdio>
#include <unordered_map>
#include <string>
#include <sstream>
using namespace std;
int main() {
string line;
while (getline(cin, line)) {
istringstream linestream(line);
int sum=0;
int x;
while (linestream >> x) {
sum += x;
}
cout<<sum<<endl;
}
}
分类: 算法
bitmap
除了将bitmap转化为int之类的,还可以直接用string.
同时可以用unordered_map<string,bool>记录状态是否访问过。
同时注意string的一个char的范围不只是0、1,因此还可以用来计数之类的
dfs可能可行的减少时间方法
- 记录 已经搜索过的节点 的结果值。当下次访问到这个节点时,直接返回。
- 将当前已经进行过的步骤,与 已经计算出来的、可能不是最优解的 结果 进行对比,以便剪枝。
- 将节点排好序,以便2.能够发挥更好的剪枝效果
dfs思考
dfs可以分两种,一种自上而下的将结果传递给下层,如采用already_count之类的.
第二种则是像这样:dfs()返回整个问题的答案,而一个问题可以分解为多个子问题。如果可以这样,那么这样更好,因为感觉像这样dfs更容易缕清思路。
dfs时注意记忆化。
dfs时,一种更好的实践是在父节点要向下递归时,不判断子节点是否为空。而是在访问当前节点时,判断是否为空。这种将事情推给子节点自己去判断 比较思路清晰。
dfs时超时,可考虑将原先的数据排序,看能否加快
bfs 新思索
最近继续做bfs tag的题,有了一些新的思索。
- 每次bfs的时候,我们总把新元素放到队列后面。但其实我们常常遇到的这种情况 是另外一种情况的简化版,在那一种hard模式的版本中,队列是用的双端队列。
- 若新元素与当前相比 并没有实际的进展,则新元素被放到队首,反之放到队尾。这时,整个双端队列分为两级、分为两部分,前面的是同一级的,后面的下一级的。具体题型见lc1263 推箱子
2. 在bfs时,我们使用vis来标记某个元素是否曾经访问过。在一个图中,若这种遍历边 并 判断是否曾经访问过的行为就有很多时间开销了,那我们就可以采用list来记录某个节点的边们,并将访问过的边去掉,具体题目见lc1345 跳跃游戏IV。list相关代码如下:
auto it = list.begin();
while (it != list.end()) {
int next_x = (*it);
if (!vis[next_x]) {
vis[next_x] = true;
q.push(next_x);
if (next_x == desi) {
return step;
}
list.erase(it++);
} else {
it++;
}
}
算法模板
spfa记录路径
记录录路径:开一个pre数组,在每次松弛bai边的时候(就是在执行duif d[j]+cost[k]<d[i] then d[i]:=d[j]+cost[k]的时候),这时如果d[i]被更新了,就将pre[i]:=j,表示当前到zhii点的最短路dao径中,j是i的前驱结点。在做完spfa时,
对于结点i,不断地i:=pre[i]直到i=源点,这途中的点即为路径。
判断负权环:因为用了队列版维护d数组,如果i结点入队次数大于等于n次,则有负权权环
代码简化
若需要一个类,这个类只有成员变量且【成员变量类型相同】,那么可以直接pair或者数组
bfs vis数组 最短路
vis的目的其实并不是已经访问过哪些节点,而是记录【[在当前步骤] 或 [之前的步骤]】中已经访问过的状态。因为之前已经访问过了这些状态,而之前那些访问用的步骤更少,因此之后就没必要再次访问这些状态了。
当用bfs用于记录到达某个状态的步骤时,则若某个节点 【曾经访问过】或【通步骤的其他状态已经将这个节点加入队列】,则不用再将这个节点加入队列:
while (!sourceq.empty()) {
level_count = sourceq.size();
while ((level_count--) > 0) {
auto p = sourceq.front();
sourceq.pop();
for (int i=0; i<4; i++) {
int subx = p.first + dx[i];
int suby = p.second + dy[i];
if (subx >= 0 && suby >= 0 && subx < m && suby < n) {
if (A[subx][suby] == 1) {
return step;
} else if (A[subx][suby] == 0 && vis2[subx][suby] == 0){
sourceq.push({subx,suby});
vis2[subx][suby]=1;
}
}
}
}
或者:
while (!sourceq.empty()) {
level_count = sourceq.size();
while ((level_count--) > 0) {
auto p = sourceq.front();
sourceq.pop();
if (vis2[p.first][p.second] == 1) {
continue;
}
vis2[p.first][p.second] = 1;
for (int i=0; i<4; i++) {
int subx = p.first + dx[i];
int suby = p.second + dy[i];
if (subx >= 0 && suby >= 0 && subx < m && suby < n) {
if (A[subx][suby] == 1) {
return step;
} else if (A[subx][suby] == 0){
sourceq.push({subx,suby});
}
}
}
}
step++;
}
但不能这样:
while (!sourceq.empty()) {
level_count = sourceq.size();
while ((level_count--) > 0) {
auto p = sourceq.front();
sourceq.pop();
// 1:
if (vis2[p.first][p.second] == 1) {
continue;
}
vis2[p.first][p.second] = 1;
for (int i=0; i<4; i++) {
int subx = p.first + dx[i];
int suby = p.second + dy[i];
if (subx >= 0 && suby >= 0 && subx < m && suby < n) {
if (A[subx][suby] == 1) {
return step;
} else if (A[subx][suby] == 0 && vis2[subx][suby] == 0){
// 2:
sourceq.push({subx,suby});
vis2[subx][suby]=1;
}
}
}
}
这样做的话,因为2处已经将vis2[subx][suby]设置为1,则访问这个节点时,因为1处代码的存在,会直接跳过访问这个节点。
另外,若有多个起始状态的bfs出现了tle,可能的原因是:队列中的起始状态有重复的,不妨试试没有重复时可不可以度过难关。做leetcode::934::最短的桥时就因为这种原因tle了
dfs bfs思考
dfs时传入了第一个参数。这时就像下棋选择了走的第一步。接着dfs会遍历所有可能的情况。就像只确定下棋的第一步时,之后不同走法可能会走到同一位置一样,dfs也会多次到达同一位置。
因此若只需要到达某个位置一次,则用于一个dfs函数外的数组vis记录访问过的,访问过的就vis[x][y]=1。就算某次递归栈到达了某个地方,但因为这个地方曾经被访问过,进而从这个地方开始的所有其他可能也都曾被访问过,那么就无需再次访问这个地方,这次dfs直接return。
若并非只需要到达某个位置,而是想要知道有多少种到达目标位置的方式。那么标记的方式应该是:在进入dfs函数时令vis[x][y]=1, 在退出dfs时令vis[x][y]=0,通过这种方式避免dfs在一次访问中重复访问某个点。
对bfs进行思考时,用一个长宽为2的地图进行思考。