2022暑假每日一题笔记(二)

T1--4276. 擅长C

题目较长,参考原题。
#include <bits/stdc++.h>
using namespace std;
char g[26][7][5];

int main(){
    for (int i = 0;i < 26;i ++)
        for (int j = 0;j < 7;j ++)
            cin >> g[i][j];
    
    string word;
    vector<string> words;
    char c; // 最后一行给出的句子可能以空格分隔,所以用getchar
    while ((c = getchar()) != -1){
        if (isupper(c)) word += c;
        else{
            if (!word.empty()) words.push_back(word);
            word = "";
        }
    }
    
    for (int i = 0;i < words.size();i ++){
        if (i) cout << '\n';
        for (int j = 0;j < 7;j ++){
            for (int k = 0;k < words[i].size();k ++){
                if (k) cout << ' ';
                for (int t = 0;t < 5;t ++) cout << g[words[i][k]-'A'][j][t];
            }
            cout << '\n';
        }
        // cout << words[i] << '\n';
    }
    return 0;
}

T2--4277. 区块反转

给定一个单链表 L,我们将每 K 个结点看成一个区块(链表最后若不足 K 个结点,也看成一个区块),请编写程序将 L 中所有区块的链接反转。
例如:给定 L 为 12345678,K 为 3,则输出应该为 78456123。

补充
本题中可能包含不在单链表中的节点,这些节点无需考虑。

输入格式
第 1 行给出第 1 个结点的地址、结点总个数正整数 N、以及正整数 K,即区块的大小。结点的地址是 5 位非负整数(可能包含前导 0),NULL 地址用 −1 表示。
接下来有 N 行,每行格式为:
Address Data Next
其中 Address 是结点地址,Data 是该结点保存的整数数据,Next 是下一结点的地址。

输出格式
对每个测试用例,顺序输出反转后的链表,其上每个结点占一行,格式与输入相同。

数据范围
1≤K≤N≤10^5
输入样例:
00100 8 3
71120 7 88666
00000 4 99999
00100 1 12309
68237 6 71120
33218 3 00000
99999 5 68237
88666 8 -1
12309 2 33218
输出样例:
71120 7 88666
88666 8 00000
00000 4 99999
99999 5 68237
68237 6 00100
00100 1 12309
12309 2 33218
33218 3 -1

关于数组模拟链表的写法参考:https://www.acwing.com/problem/content/description/4276/。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6;
int h,n,m;
int val[N],ne[N];

struct Node{
    int addr,data,next;
};

int main(){
    cin >> h >> n >> m;
    
    int addr,data,next;
    for (int i = 0;i < n;i ++){
        cin >> addr >> data >> next;
        val[addr] = data,ne[addr] = next;
    }
    
    vector<Node> a;
    for (int i = h;i != -1;i = ne[i]){
        a.push_back({i,val[i],ne[i]});
    }
    
    vector<vector<Node>> ans;
    vector<Node> v;
    for (int i = 0,j = 1;i < a.size();i ++,j ++){
        v.push_back(a[i]);
        if (j == m || i == a.size()-1) ans.push_back(v),v.clear(),j = 0;
    }
    
    reverse(ans.begin(),ans.end());
    for (int i = 0;i < ans.size();i ++){
        for (int j = 0;j < ans[i].size();j ++){
            printf("%05d %d ",ans[i][j].addr,ans[i][j].data);
            if (j == ans[i].size()-1){
                if (i < ans.size()-1) printf("%05d\n",ans[i+1][0].addr);
                else printf("-1\n");
            }
            else printf("%05d\n",ans[i][j].next);
        }
    }
    return 0;
}

T3--4278. 峰会

峰会是国家元首或政府首脑的会议。
为峰会安排休息区可不是一件简单的工作。
一共有 N 个首脑参加峰会,编号 1∼N。
这些首脑之间存在 M 对两两之间的直接朋友关系。
在划分区域时,我们希望被安排在同一休息区域的首脑们满足,任意两人之间都是直接朋友关系。
现在,给定 K 个关于划分休息区域的安排,请你依次判断每个安排是否合理。

输入格式
第一行包含两个整数 N 和 M。
接下来 M 行,每行包含两个整数 a,b,表示首脑 a 和首脑 b 之间存在直接朋友关系。
再一行包含整数 K。
接下来 K 行,每行描述一个区域安排,首先包含一个整数 L,表示该安排打算将 L 个首脑安排在同一区域休息,然后包含 L 个整数,表示这些首脑的编号。

输出格式
共 K 行,第 i 行输出对第 i 个安排的判断,具体格式为
如果安排满足其中的任意两人之间都是直接朋友关系并且不存在额外的人与被安排的所有人都是直接朋友关系(即无法安排更多的人在这一区域休息),则输出 Area X is OK.
如果安排满足其中的任意两人之间都是直接朋友关系并且存在额外的人与被安排的所有人都是直接朋友关系(即可以安排更多的人在这一区域休息),则输出 Area X may invite more people, such as H.,其中 H 是额外可被安排的人的编号(如果不唯一,则输出最小的那个)。
如果安排无法满足其中的任意两人之间都是直接朋友关系,则输出 Area X needs help.。
X 表示组别编号,从 1 到 K。

数据范围
1≤N≤200,
1≤M≤N(N−1)/2,
1≤a,b≤N,
a≠b,
1≤K≤100,
1≤L≤N,
同一对直接朋友关系不会在输入中重复出现。

输入样例:
8 10
5 6
7 8
6 4
3 6
4 5
2 3
8 2
2 7
5 3
3 4
6
4 5 4 3 6
3 2 8 7
2 2 3
1 1
2 4 6
3 3 2 1
输出样例:
Area 1 is OK.
Area 2 is OK.
Area 3 is OK.
Area 4 is OK.
Area 5 may invite more people, such as 3.
Area 6 needs help.

直接枚举每个区域,模拟看看是否满足条件。

#include <bits/stdc++.h>
using namespace std;
const int N = 205;
bool g[N][N];
int n,m,c;

int main(){
    cin >> n >> m;
    int a,b;
    for (int i = 0;i < m;i ++){
        cin >> a >> b;
        g[a][b] = g[b][a] = true;
    }
    
    cin >> c;int t,x;
    for (int i = 0;i < c;i ++){
        vector<int> v;
        unordered_set<int> S;
        
        cin >> t;
        for (int j = 0;j < t;j ++){
            cin >> x;
            v.push_back(x);
            S.insert(x);
        }
        
        bool flag = true;
        for (int j = 0;j < v.size();j ++){
            for (int k = j + 1;k < v.size();k ++){
                if (!g[v[j]][v[k]]){
                    flag = false;break;
                }
            }
        }
        if (!flag) printf("Area %d needs help.\n",i+1);
        else{
            bool x = false;
            for (int j = 1;j <= n;j ++){
                if (S.count(j)) continue;
                bool flag = true;
                for (int k = 0;k < v.size();k ++){
                    if (!g[j][v[k]]){
                        flag = false;break;
                    }
                }
                if (flag){
                    x = true;
                    printf("Area %d may invite more people, such as %d.\n",i+1,j);
                    break;
                }
            }
            if (!x) printf("Area %d is OK.\n",i+1);
        }
    }
    return 0;
}

T4--4279. 笛卡尔树

笛卡尔树 是由一系列不同数字构成的二叉树。
树满足堆的性质,中序遍历返回原始序列。
最小笛卡尔树表示满足小根堆性质的笛卡尔树。

例如,给定序列 {8,15,3,4,1,5,12,10,18,6},则生成的最小堆笛卡尔树如图所示。

现在,给定一个长度为 N 的原始序列,请你生成最小堆笛卡尔树,并输出其层序遍历序列。

输入格式
第一行包含整数 N。
第二行包含 N 个两两不同的整数,表示原始序列。

输出格式
共一行,输出最小堆笛卡尔树的层序遍历序列。

数据范围
1≤N≤30,
原始序列中元素的取值范围 [−2147483648,2147483647]。

输入样例:
10
8 15 3 4 1 5 12 10 18 6
输出样例:
1 3 5 8 4 6 15 10 12 18

树的题,不太会,参考y总讲解。
考察笛卡尔树的定义,它首先是一个最小堆,所以根就是序列中的最小值,由于它的中序遍历和生成序列一致,所以在生成序列中,根左侧的数也就在笛卡尔树的左侧,右侧同理。
然后对左、右子树递归下去,就可以生成笛卡尔树了。

然后考虑算法实现,首先根据上面结论,DFS生成笛卡尔树,然后对树层次遍历输出答案。
上面是分开处理的写法,也可以在建树的同时记录每层的元素,等效于另外做层次遍历。

#include <bits/stdc++.h>
using namespace std;
const int N = 35;
int g[N];
vector<int> level[10];

int getmin(int l,int r){
    int res = l;
    for (int i = l+1;i <= r;i ++)
        if (g[i] < g[res]) res = i;
    return res;
}

void dfs(int l,int r,int d){ // [l,r]表示建树区间,d表示当前深度
    if (l > r) return;
    int root = getmin(l,r);
    level[d].push_back(root);
    dfs(l,root-1,d+1),dfs(root+1,r,d+1);
}

int main(){
    int n;
    cin >> n;
    
    for (int i = 0;i < n;i ++) cin >> g[i];
    dfs(0,n-1,0);
    for (int i = 0;level[i].size();i ++){
        for (auto it : level[i]) cout << g[it] << ' ';
    }
    return 0;
}

T5--691. 立方体IV

Vincenzo 决定制作立方体 IV,但所有预算只够制作一个正方形迷宫。
它是一个完美的迷宫,每个房间都呈正方形,并具有 4 扇门(四个边一边 1 个)。
每个房间里都有一个号码。
一个人只有在下一个房间的号码比当前房间的号码大 1 的情况下,才能从当前房间移动到下一个房间。
现在,Vincenzo 为所有房间分配了唯一的号码(1,2,3,…S2)然后将 S2 个人放在了迷宫中,每个房间 1 个,其中 S 是迷宫的边长。
能够移动次数最多的人将获胜。
弄清楚谁将成为赢家,以及他将能够到达的房间数量。

输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组测试数据第一行包含整数 S,表示迷宫的边长。
接下来 S 行,每行包含 S 个整数,表示具体的迷宫的房间号分布,需注意 1,2,3,…S2 这 S2 个数字,每个数字只出现一次。

输出格式
每组数据输出一个结果,每个结果占一行。
结果表示为 Case #x: r d,其中 x 是组别编号(从 1 开始),r 是获胜的人最初所在房间的房间号,d 是他可以到达的房间数量。
如果有多个人可到达的房间数相同,那么最初所在房间的房间号最小的人将获胜。

数据范围
1≤T≤100,
1≤S≤1000
输入样例:
2
2
3 4
1 2 


3
1 2 9 
5 3 8 
4 6 7 
输出样例:
Case #1: 1 2
Case #2: 6 4

不会。本题和【901. 滑雪】类似,用到记忆化搜索(DFS+DP),模板题。

要确定是否能用DP计算,还要分析DP状态间的转移是否存在环,如果存在环则不能用DP。
本题中不存在环,这是显然的,反证法可以证明。

#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int n;
int g[N][N],f[N][N];
int dx[] = {0,0,1,-1},dy[] = {1,-1,0,0};

int dfs(int x,int y){
    int &v = f[x][y];
    if (v != -1) return v;
    v = 1; // 合法的起始点的初始路径长度为1
    
    for (int i = 0;i < 4;i ++){
        int a = x + dx[i],b = y + dy[i];
        // DP状态表示:f[i,j]表示以(i,j)为起点的最大合法路径长度
        // 从(x,y)走到(a,b),f[x,y] = f[a,b] + 1
        if (a >= 0 && a < n && b >= 0 && b < n && g[a][b] - 1 == g[x][y]){
            v = max(v,dfs(a,b)+1);
        }
    }
    return v;
}

int main(){
    int t;
    cin >> t;
    for (int x = 1;x <= t;x ++){
        cin >> n;
        for (int i = 0;i < n;i ++)
            for (int j = 0;j < n;j ++)
                cin >> g[i][j];
        
        memset(f,-1,sizeof f); // 记忆化搜索        
        int cnt = 0,id = 1005;
        for (int i = 0;i < n;i ++){
            for (int j = 0;j < n;j ++){
                int t = dfs(i,j);
                if (t > cnt) cnt = t,id = g[i][j];
                else if (t == cnt) id = min(id,g[i][j]);
            }
        }
        printf("Case #%d: %d %d\n",x,id,cnt);
    }
    return 0;
}

T6--3311. 最长算术

一个算术数组是指至少包含两个整数,且相邻整数之间的差值都相等的整数数组。
例如,[910],[333] 和 [9753] 是算术数组,而 [1337],[212],和 [124] 不是算术数组。
Sarasvati 有一个包含 N 个非负整数的数组,其中的第 i 个整数为 Ai。
她想从数组中选择一个最大长度的连续算术子数组。
请帮助她确定最长的连续算术子数组的长度。

输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组数据第一行包含整数 N。
第二行包含 N 个整数,其中第 i 个整数表示 Ai。

输出格式
每组数据输出一个结果,每个结果占一行。
结果表示为 Case #x: y,其中 x 为组别编号(从 1 开始),y 表示最长的连续算术子数组的长度。

数据范围
1≤T≤100,
1≤Ai≤10^9,
对于每个测试点,满足 2≤N≤2×105 的数据一定不超过 10 组,其余数据则满足 2≤N≤2000。

输入样例:
4
7
10 7 4 6 8 10 11
4
9 7 5 3
9
5 5 4 5 5 5 4 5 6
10
5 4 3 2 1 2 3 4 5 6
输出样例:
Case #1: 4
Case #2: 4
Case #3: 3
Case #4: 6
样例解释
对于测试数据 1,最长的连续算术子数组为 [4,6,8,10]。
对于测试数据 2,最长的连续算术子数组就是数组本身。
对于测试数据 3,最长的连续算术子数组为 [4,5,6] 和 [5,5,5]。
对于测试数据 4,最长的连续算术子数组为 [1,2,3,4,5,6]。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
int b[N],n;

int main(){
    int t;
    cin >> t;
    for (int x = 1;x <= t;x ++){
        cin >> n;
        for (int i = 1;i <= n;i ++){
            cin >> b[i];
        }
        
        b[0] = b[1];
        int len = 1;
        for (int i = 1,j = 2;i <= n;i ++){
            int x = b[i] - b[i-1];
            j = i+1;
            while (j <= n && b[j] - b[j-1] == x) j ++;
            len = max(len,j-i+1);
            i = j-1;
        }
        printf("Case #%d: %d\n",x,len);
    }
    return 0;
}
posted @   grant_drew  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示