题目列表
ACW 756. 蛇形矩阵
题面
输入两个整数 n n n和 m m m,输出一个 n n n行 m m m列的矩阵,将数字 1 ∼ n × m 1\sim n\times m 1∼n×m 按照回字蛇形填充至矩阵中.
解析
定义方向数组,每次循环到边界转移到下一个方向继续输出,直到输出所有的数值
AC代码
#include<iostream>
#include<cstring>
using namespace std;
const int N=105;
int n, m, tot;
int ans[N][N];
int dirs[4][2]={{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
int main(){
memset(ans, 0x00, sizeof ans);
cin>>n>>m;
tot=n*m;
for(int x=0, y=0, k=1, d=0; k<=tot; ++k){
ans[x][y]=k;
int nx=x+dirs[d][0];
int ny=y+dirs[d][1];
if(nx<0 || nx>=n || ny<0 || ny>=m || ans[nx][ny]){ // 确认新方向
d=(d+1)%4;
nx=x+dirs[d][0];
ny=y+dirs[d][1];
}
x=nx, y=ny;
}
for(int i=0; i<n; ++i){
for(int j=0; j<m; ++j)
cout<<ans[i][j]<<" ";
cout<<endl;
}
return 0;
}
ACW 1453. 单链表快速排序
题面
给定一个单链表,请使用快速排序算法对其排序。
要求:期望平均时间复杂度为
O
(
n
log
n
)
\mathcal{O}(n\log n)
O(nlogn),期望额外空间复杂度为
O
(
log
n
)
\mathcal{O}(\log n)
O(logn).
思考题: 如果只能改变链表结构,不能修改每个节点的val值该如何做呢?
解析
链表快排,和quick sort
思路一致,先确定一个pivot
,建立三个子链表left
,mid
,left
分别存储小于pivot
的节点,等于pivot
的节点和大于pivot
的节点. 递归左右子链表后再拼接子链表.
AC代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
// 定位尾节点
ListNode* get_tail(ListNode* head){
while(head->next) head=head->next;
return head;
}
ListNode* quickSortList(ListNode* head) {
if(!head || !head->next) return head;
// 定义三个子链表
ListNode *left=new ListNode(-1); ListNode *pl=left;
ListNode *mid=new ListNode(-1); ListNode *pm=mid;
ListNode *right=new ListNode(-1); ListNode *pr=right;
int val=head->val;
// 分配元素
for(auto p=head; p; p=p->next){
if(p->val<val) pl=pl->next=p;
else if(p->val==val) pm=pm->next=p;
else pr=pr->next=p;
}
// 设置尾节点为nullptr
pl->next=pm->next=pr->next=nullptr;
// 递归左右侧
left->next=quickSortList(left->next);
right->next=quickSortList(right->next);
// 拼接链表
get_tail(left)->next=mid->next;
get_tail(left)->next=right->next;
// 释放空间
auto p=left->next;
delete left; delete mid; delete right;
return p;
}
};
ACW 1452. 矩阵极小值
题面
给定一个
n
×
n
n\times n
n×n的矩阵,矩阵中包含
n
×
n
n\times n
n×n个互不相同 的整数。
定义极小值:如果一个数的值比与它相邻的所有数字的值都小,则这个数值就被称为极小值。
一个数的相邻数字是指其上下左右四个方向相邻的四个数字,另外注意,处于边界或角落的数的相邻数字可能少于四个。
要求在KaTeX parse error: Undefined control sequence: \logn at position 14: \mathcal{O}(n\̲l̲o̲g̲n̲)的时间复杂度之内找出任意一个极小值的位置,并输出它在第几行第几列。
本题中矩阵是隐藏的,你可以通过我们预设的函数query
来获得矩阵中某个位置的数值是多少。
例如,query(a,b)
即可获得矩阵中第 a
行第 b
列的位置的数值。
解析
时间复杂度提示使用二分法求解,从中间一列(int mid=l+r>>1
)找出最小值,考虑最小值两侧的元素
V
V
V,如果左侧元素
L
<
V
L<V
L<V则在左侧寻找,如果右侧元素
R
<
V
R<V
R<V则在右侧寻找;继续二分求解.
AC代码
// Forward declaration of queryAPI.
// int query(int x, int y);
// return int means matrix[x][y].
typedef long long LL;
const int INF=0x3f3f3f3f;
class Solution {
public:
vector<int> getMinimumValue(int n) {
int l=0, r=n-1; // 对列进行二分搜索
while(l<r){
int mid=l+r>>1;
int k; // 定位mid列中最小元素所在行
LL val=INF; // mid列中最小元素的值
for(int i=0; i<n; ++i){ // 定位mid列的最小元素
int t=query(i, mid);
if(t<val){
val=t;
k=i;
}
}
// 检查mid列最小元素的左右侧
LL left=mid? query(k, mid-1):INF;
LL right=mid+1<n?query(k, mid+1):INF;
// 如果当前元素小于左右侧元素,则返回
if(val<left && val<right) return {k, mid};
if(left<val) r=mid-1;
else l=mid+1;
}
// 找到列的最小元素
int k;
LL val=INF;
for(int i=0; i<n; ++i){
int t=query(i, r);
if(t<val){
val=t;
k=i;
}
}
return {k, r};
}
};
ACW 1048. 鸡蛋的硬度
题面
最近XX公司举办了一个奇怪的比赛:鸡蛋硬度之王争霸赛。
参赛者是来自世界各地的母鸡,比赛的内容是看谁下的蛋最硬,更奇怪的是XX公司并不使用什么精密仪器来测量蛋的硬度,他们采用了一种最老土的办法–从高度扔鸡蛋–来测试鸡蛋的硬度,如果一次母鸡下的蛋从高楼的第a层摔下来没摔破,但是从a+1层摔下来时摔破了,那么就说这只母鸡的鸡蛋的硬度是a。
你当然可以找出各种理由说明这种方法不科学,比如同一只母鸡下的蛋硬度可能不一样等等,但是这不影响XX公司的争霸赛,因为他们只是为了吸引大家的眼球,一个个鸡蛋从100 层的高楼上掉下来的时候,这情景还是能吸引很多人驻足观看的,当然,XX公司也绝不会忘记在高楼上挂一条幅,写上“XX公司”的字样–这比赛不过是XX公司的一个另类广告而已。
勤于思考的小A总是能从一件事情中发现一个数学问题,这件事也不例外。
“假如有很多同样硬度的鸡蛋,那么我可以用二分的办法用最少的次数测出鸡蛋的硬度”,小A对自己的这个结论感到很满意,不过很快麻烦来了,“但是,假如我的鸡蛋不够用呢,比如我只有1个鸡蛋,那么我就不得不从第1层楼开始一层一层的扔,最坏情况下我要扔100次。如果有2个鸡蛋,那么就从2层楼开始的地方扔……等等,不对,好像应该从1/3的地方开始扔才对,嗯,好像也不一定啊……3个鸡蛋怎么办,4个,5个,更多呢……”,和往常一样,小A又陷入了一个思维僵局,与其说他是勤于思考,不如说他是喜欢自找麻烦。
好吧,既然麻烦来了,就得有人去解决,小A的麻烦就靠你来解决了。
解析
方法1:动态规划,时间复杂度
O
(
n
2
m
)
\mathcal{O}(n^2m)
O(n2m)
定义
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示测算楼层高度为
i
i
i, 当前可用鸡蛋数量为
j
j
j,需要的测量次数,枚举当前试验所在层
k
k
k,其中
1
≤
k
≤
i
1\leq k\leq i
1≤k≤i,讨论试验结果,如果鸡蛋碎了,则下移至区间高度为
k
−
1
k-1
k−1进行试验,并且可用鸡蛋数量减一(
j
−
1
j-1
j−1);如果鸡蛋没碎,则上移至区间高度为
i
−
k
i-k
i−k进行试验,因此得到状态转移方程
f
[
i
]
[
j
]
=
max
(
f
[
k
−
1
]
[
j
−
1
]
,
f
[
i
−
k
]
[
j
]
)
+
1
f[i][j]=\max(f[k-1][j-1], f[i-k][j])+1
f[i][j]=max(f[k−1][j−1],f[i−k][j])+1
方法2:动态规划,时间复杂度
O
(
n
m
)
\mathcal{O}(nm)
O(nm)
定义
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示使用
j
j
j个鸡蛋测量
i
i
i次最多可以测量的高度区间,可以根据试验结果分为两个独立的集合,因此状态转移方程为
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
+
f
[
i
−
1
]
[
j
−
1
]
+
1
f[i][j]=f[i-1][j]+f[i-1][j-1]+1
f[i][j]=f[i−1][j]+f[i−1][j−1]+1
AC代码:DP
#include<cstring>
#include<iostream>
using namespace std;
const int N=105;
const int M=15;
int f[N][M];
int n, m;
int main(){
while(cin>>n>>m){
for(int i=1; i<=n; ++i) f[i][1]=i; // 区间高度为i, 只有一个鸡蛋, 需要测量i次
for(int i=1; i<=m; ++i) f[1][i]=1; // 区间高度为1, 只要测量1次即可
for(int i=2; i<=n; ++i){
for(int j=2; j<=m; ++j){
f[i][j]=f[i][j-1];
for(int k=1; k<=i; ++k)
f[i][j]=min(f[i][j], max(f[k-1][j-1], f[i-k][j])+1);
}
}
cout<<f[n][m]<<endl;
}
return 0;
}
AC代码:DP
#include<cstring>
#include<iostream>
using namespace std;
const int N=105;
const int M=15;
int f[N][M];
int n, m;
int main(){
memset(f, 0x00, sizeof f);
while(cin>>n>>m){
for(int i=1; i<=n; ++i){
for(int j=1; j<=m; ++j){
f[i][j]=f[i-1][j]+f[i-1][j-1]+1;
}
if(f[i][m]>=n) {cout<<i<<endl; break;}
}
}
return 0;
}