编程学习笔记---7月

07.19

1、Heap Paths

从根节点往下,值是单调的。
任务是检查完全二叉树的每条路径,检查这个数据结构是否是堆。

思想:
(1)把输入的值构造成一个完全二叉树。
(2)输出所有的路径,并判断是否是堆。

步骤:
(1)堆的创建。
参考二叉树,如果存储在数组中,按层存储。
头文件 #include <algorithm>
但是先用数组实现了

(2)判断是否是堆
计算叶子结点的个数,即为路径的总数
(思路:先找出来上一层满的有多少)
(3)补充:调整堆
AdjustHeap()

(4)补充:堆的应用:插入一个元素/删除最大的元素

卡在:不知道如何判断有多少条路径
不知道如何按次序输出子节点

参考别人的答案
https://www.liuchuo.net/archives/8027
不知道如何按次序输出子节点:解决:算法 先序遍历的镜像
不知道如何判断有多少条路径:也不用判断,先序遍历输出即可。

07.21

0、dfs
回溯法
考察点:dfs

#include <iostream>
#include <vector>

using namespace std;

vector<int> v;
int a[1009], n, isMin = 1, isMax = 1; //两个标志为,最后最多只有一个为1.
//a[]大小设为1001即可,因为a[0]一般不存储结点,从a[1]开始存储。

void dfs(int index) { //根据index,表示这个index结构下面的都会被调用了

    if (index * 2 > n && index * 2 + 1 > n) { //检查是否到最下面了,如果到了就输出
        if (index <= n) {  //这个用来判断是不是空的某个子节点也进来了
            for (int i = 0; i < v.size(); i++)
                printf("%d%s", v[i], i != v.size() - 1 ? " " : "\n");  //v[i]是按照节点在栈中排序了的,如果i输出的这个节点不是最后的根结点,就输出空格,如果是最后的,就输出换行
        }
    } else {
        v.push_back(a[index * 2 + 1]); //如果不是到最下面了,就先右边,再左边(根据题目来的)
        dfs(index * 2 + 1);
        v.pop_back();
        v.push_back(a[index * 2]);
        dfs(index * 2);
        v.pop_back();
    }
}
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]); //输入
    v.push_back(a[1]);  首先把a[1]入栈
    dfs(1);    //调用了这一句,其他就都会递归调用了。

    for (int i = 2; i <= n; i++) {
        if (a[i/2] > a[i]) isMin = 0; //从否定的方面来判断标志位
        if (a[i/2] < a[i]) isMax = 0;
    }
    if (isMin == 1)
        printf("Min Heap");
    else
        printf("%s", isMax == 1 ? "Max Heap" : "Not Heap");
    return 0;
}

收获:
(1)长度一般固定一个最大值,N+1即可
(2)dfs下标一般从1开始
(3)变量一般设置为全局变量,好调用
(4)main函数调用一次即可,dfs函数中,先判断是否要输出,如果不输出,把自己的左子树和右子树递归调用。调用的规格是:push,dfs,pop
(5)可能有种特殊情况,只有左子树,但是把空的右子树也调用进去了。这种只会在栈中有变化,输出的时候判断不满足边界条件就不输出它了。
(6)判断边界:

  • 检查是否是叶子结点:index * 2 > n && index * 2 + 1 > n
    如果成立的话,就是叶子结点,输出它。如果不成立,再递归调用左子树和右子树。
  • 检查是否是空的右子树:index <= n
    如果满足条件的话,是真实存在的结点,如果不满足条件,说明这个是空了的结点。

1、Vertex Coloring
顶点着色,相邻的点颜色不同。需要做的是:检查它给出的情况是否满足每条边的两个顶点颜色不同。

  • 需要用到set。其中的元素是有序且不重复的。用set统计颜色的数目。
#include <iostream>
#include <vector> //栈结构,用来存边对应的两个结点。这里不突出栈结构,只是用来存放
#include <set>//可以直接排序,并且不重复
using namespace std;
struct node { int t1, t2; }; //每条边对应的两个结点的结构
int main() {
    int n, m, k, i;
    cin >> n >> m;
    vector<node> v(m);
    for (i = 0; i < m; i++) cin >> v[i].t1 >> v[i].t2; //输入边对应的结点的信息
    cin >> k;
    while (k--) {
        int color[10009] = { 0 }; //由于边数最多是10000,所以颜色应该不会超过这个数
        bool flag = true;
        set<int> se; 
        for (i = 0; i < n; i++) {
            cin >> color[i]; //输入颜色
            se.insert(color[i]); //有序排列
        }
        for (i = 0; i < m; i++) {
            if (color[v[i].t1] == color[v[i].t2]) { //如果结点对应的颜色相同,就break
                flag = false;
                break;
            }
        }
        if (flag)
            printf("%d-coloring\n", se.size());
        else
            printf("No\n");
    }
    return 0;
}

收获:
(1)set可以用来排序。
(2)先用栈结构来存边信息,再用数组来存储每个节点的颜色。
(3)在数组中比较每个两节点的颜色是否相同,用一个标记为flag来标记结果。

2、battle over cities
猜测应该考察图的知识,并且有权重。

07.27

1、前置题---battle over cities
把 “需要维修多少个地铁” 这个问题转换成 “需要多少次 dfs 才能遍历完整个图”。

#include <iostream>
#include <string.h>

using namespace std;

#define maxn 1001

// 变量定义
int total_cities, highways, check_cities; // 总城市 城市之间的地铁 被检查的城市
bool highway_map[maxn][maxn]; // 定义地铁地图
int check_list[maxn];   // 定义检查列表用于存储检查的城市
bool checked_cities[maxn]; // 判断是否检查过这个城市

// 定义方法递归实现 dfs 遍历节点
void CheckHighway(int source_city) {
    // 没查看过就标记为已查看
    checked_cities[source_city] = true;
    // 查看当前城市连接的城市
    for (int i = 1; i <= total_cities; ++i) {
        // 若该城市没被检查过(缩短递归次数)并且 含有该城市
        if (!checked_cities[i] && highway_map[source_city][i])
            CheckHighway(i);  //可以实现深度优先搜索。只要是和这个城市相连的并且没有被检查过,就检查它。并递归调用检查它的节点。
    }

}

int main() {
    // 初始化变量
    scanf("%d%d%d", &total_cities, &highways, &check_cities);

    for (int i = 0; i < highways; ++i) {             // 构建城市地铁图
        int source_city, aim_city;  // 定义变量接收 源城市 目标城市
        scanf("%d%d", &source_city, &aim_city);
        highway_map[source_city][aim_city] = true; // 将两个城市对应的值均设置为 true,表示两城市是连通的
        highway_map[aim_city][source_city] = true;
    }

    for (int i = 0; i < check_cities; ++i) {    // 构建检查城市列表
        scanf("%d", &check_list[i]);
    }

    // 计算需要维修公路的数量
    int times = 0; // 计算需要维修多少条公路
    for (int i = 0; i < check_cities; ++i) {    // 根据需要检查的城市数量检查城市
        times = 0;
        memset(checked_cities, false, sizeof(checked_cities)); // 初始化已检查过的城市为 false
        checked_cities[check_list[i]] = true; // 将被掠夺的城市设置为 true
        for (int j = 1; j <= total_cities; ++j) {
            if(!checked_cities[j]){ //只要有城市没检查,就说明有分离的城市了,需要修建一条路
                times++;
                CheckHighway(j); //把与这条城市想通的所有的城市都遍历了。没遍历的说明不相连,需要新修建一条路。
            }
        }
        printf("%d\n", times-1);
        
    }
    return 0;
}

再做这道题的hard版

带了权重,需要测试丢失掉每个城市之后,再把图连接起来需要的费用。

方法1:最小生成树
最小生成树有一个Prime算法

用到的头文件 iostream vector
arc是cost矩阵,用来记录开销。首先置为大数。然后赋值。如果路是好的,不用修,开销置为0.如果路是坏的,就记录开销。

#include<iostream>
#include<vector>
#pragma warning(disable:4996)
using namespace std;
int N, M, s_max = 0;
//定义需要的N,M,


vector<int> re;
int arc[505][505];
vector<int> x;
vector<int> te;

void ins(int t)
{
    for (int i = 1;i <= N;i++)
        if (i != t) x.push_back(i);  //要删除的顶点时t,把除了t之外的所有顶点入栈于x中。
}

int main()
{
    for (int t = 0;t < 505;t++)
        for (int i = 0;i < 505;i++)
            arc[t][i] = 0x3f3f3f3f;  //把arc置为一个很大的值,然后再更新。值很大说明这条路修不了。
    cin >> N >> M;
    while (M--)
    {
        int a, b, c, d;
        scanf("%d %d %d %d", &a, &b, &c, &d);
        if (d) arc[a][b] = arc[b][a] = 0; //如果d是1,说明这条路本来就是通的,那么就不需要修建它,花费就是0.
        else arc[a][b] = arc[b][a] = c; //如果d是0,更新他的花费是c
    }
    for (int t = 1;t <= N;t++) //t分别对每个节点都进行操作
    {
        x.clear(); //x中存放的是除了当前节点的所有节点。
        te.assign(N + 1, -1); 
        ins(t); 
        int f = x.back(); //f是先从所有的节点中选取出来一个节点。(可以随机选择)
        x.pop_back(); //把它删掉
        for (int i = 0;i < x.size();i++)//初始化
                te[x[i]] = arc[f][x[i]]; //把 “当前选中的节点” 连接 “剩下的节点” 需要的cost 存储在te中。te是初始的花费,后面会进行更新。
        int eff = 0;
        while (!x.empty())//最小生成树 //如果x还没有空掉
        {
            int min_s = 0x3f3f3f3f;int v = 0;
            for (int i = 0;i < x.size();i++)
            {
                if (min_s > te[x[i]]) // 根据当前的节点,确定下一个最小路径
                {
                    min_s = te[x[i]];
                    v = i;
                }
            }
            if (min_s == 0x3f3f3f3f) { eff = min_s;break; } //如果这个点与其他的点都是孤立的,那么off = 0x3f3f3f3f,这个很大的值。
            
           eff += min_s; //eff加上刚才的这个最小的路径。
            int ttt=x[v]; //ttt是我找到的下一个顶点
            x.erase(x.begin() + v); //把下一个顶点从x中删除
            v = ttt;
            for (int i = 0;i < x.size();i++) 
                if (arc[v][x[i]] < te[x[i]]) //te中存放的应该是最小的边信息
                    te[x[i]] = arc[v][x[i]];
 //这一步太妙了。te中存放的是目前为止到x[i]这个点的最小边信息。
//是已经发生过的统计量。新加入顶点后,可能边的权重比原来的大,
//如果大,就不更新了,用原来的边。如果比原来的权重小,就进行更新。
//这样就能保证te中存放的一直是最小的路径。
        }


        if (eff> s_max)  //每一个顶点完了之后,更新s_max。
//如果更新了,说明有了比刚才更大的值,也就是有了更重要的点。需要把之前的清空,放入这个点。
        {
            s_max = eff;
            re.clear();
            re.push_back(t);
        }
        else if (eff == s_max)
            re.push_back(t);
 //如果两个点同等重要,就都入栈。
    }

    if (s_max == 0 && re.size() == N) { 
//如果去除一个点之后,剩下的cost为0,也就是说剩下的点还都是连通的,那么就输出0,这是特殊情况。
        cout << 0 << endl;exit(0);
    }
    cout << re[0];
    for (int t = 1;t < re.size();t++)
        cout << " " << re[t];
    cout << endl; 
//因为进入的时候本来就排好序了,所以如果需要输出的话,按序输出即可。
}

思路:
输入arc矩阵/从一个顶点开始/其余顶点入栈/找到当前顶点的最小边/最小边信息保存在te中/找到下一个顶点/找到下一个最小边/更新cost/看情况更新te/直到所有的顶点都计算过了。

这道题考查的点还挺多的,思路也比较复杂,很棒!

07.28

1、Business
需要考虑花费的时间、ddl、收益。
其实是01背包问题,但是没有给出数据明确的范围。用map。

对动态规划/背包问题还不够熟练,先看一些例题。

参考《王道》第七章——动态规划
(1)递归求解
上楼梯问题
相当于把所有的解通过递归方式都求出来了。重点是递推关系的确定。

稍难一点,n封信装错信封,都装错了,共有几种方式?
用数学里面的排序思想求阶乘就可以。但是这种考虑的不全面,没有考虑到两两互换的情况。
排错公式:F[n] = (n - 1) * F[n - 1] + (n - 1) * F[n - 2]。
最重要的是:求得递推关系式

(2)最长递增子序列(LIS)
用F(i)代表:若序列以ai结尾时:最长子序列的长度


EA0CAF10-0AD6-49BE-8A6B-CD080BE411B5.png

题目:拦截导弹

#include <stdio.h>

int F[26] = {1};
int high[26] = {0};


int main () {
    int k = 0,i = 0,j=0;
    scanf("%d",&k);
    for(i=1;i<=k;i++){
        scanf("%d",&high[i]);
    }
    F[1]= 1;
    for(i=2;i<=k;i++){
        for(j=1;j<i;j++){
            if(high[j]>=high[i] && (F[j] + 1 > F[i])){
                F[i] = F[j] + 1;
            }
        }
    }
    printf("%d",F[k]);
    return 0;
}

给出的答案通过一个max函数

for (int j = 1;j < i;j ++) { //遍历其前所有导弹高度
if (list[j] >= list[i]) { //若j号导弹不比当前导弹低
tmax = max(tmax,dp[j] + 1); //将当前导弹排列在以j号导弹结尾 的最长不增子序列之后,计算其长度dp[j] + 1,若大于当前最大值,则更新最大值
} }

其中dp存储的是 以当前的i结尾,最长子序列的长度。结束后再从里面找出最大的值。

所以自己原来的程序最后少了一步,应该再把F的结果进行比较,找出最大的值才是最长子序列。刚才输出结果正确知识巧合。

(3)最长公共子序列

59DD6A26-7049-4737-A674-50B105D3F88E.png

例题:输入两个字符串,长度都不超过100。输出最长子序列的长度(数字)即可。

收获:
字符串需要用到头文件 #include <string.h>
字符串用 char S[101]存储
while (scanf("%s%s",S1,S2) != EOF) 就会导致循环输入
求字符串的长度,用的是strlen()函数直接求了。
因为字符串数组下标从0开始,所以第i 个字符位置为S1[i-1],表示当前两个字符
循环也是从i开始,for i ,内嵌for j 。由于用到的j也都是之前的,所以这样循环是可以的,不会用到没定义的数据。
最后不需要再比较大小了,只用输出最末尾的数据即可。

(4)状态与状态转移方程---统一思路

之前的笔记

###2、动态规划
最后数值可能较大,存放的数组用long long型
* 后面的结果可能和前两次有关,重点是如何写出正确的逻辑公式。

* 最长递增子序列 LIS

* 最长公共子序列 LCS


if (S1[i - 1] != S2[j - 1]) //因为字符串数组下标从0开始,所以第i 个字符位置为S1[i-1],若当前两个字符不相等
        dp[i][j] = max(dp[i][j - 1],dp[i - 1][j]); //dp[i][j]为 dp[i][j - 1] 和 dp[i - 1][j]中较大的一个
else 
        dp[i][j] = dp[i - 1][j - 1] + 1; //若它们相等,则dp[i][j] 比dp[i - 1][j - 1]再加一

https://pintia.cn/problem-sets/994805342720868352/problems/type/7?page=1

============================================

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351