ECNU“算法设计与分析”课程OJ平台题解及总结。
以下各图片均来源于:https://acm.ecnu.edu.cn/
A.框体排列
简单题,贪心模拟即可,注意坐标需要预先排序。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
// greedy
int n, k;
cin >> n >> k;
vector<int> v(n);
for (int i = 0; i < n; i++)
{
cin >> v[i];
}
sort(v.begin(), v.end());
int res = 1;
int last = v[0];
for (int i = 1; i < n; i++)
{
if (v[i] - last <= k)
{
continue;
}
res++;
last = v[i];
}
cout << res << endl;
return 0;
}
|
B. 整数排序
定义为字符串类型,重定义排序规则连接即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
bool cmp(string a, string b)
{
return a + b > b + a;
}
int main()
{
int n;
cin >> n;
vector<string> v(n);
for (int i = 0; i < n; i++)
{
cin >> v[i];
}
sort(v.begin(), v.end(), cmp);
string res;
for (int i = 0; i < n; i++)
{
res += v[i];
}
if (res[0] == '0')
{
cout << 0 << endl;
return 0;
}
cout << res << endl;
return 0;
}
|
- 时间复杂度:O(nlogn)
- 空间复杂度:O(nlogn)
C. 选择排序(构造)
https://acm.ecnu.edu.cn/problem/3607/
其实很简单的一道题,但那天中午脑子不清醒。N定义为10^5+5
是对的。输出应该为[N,1]或[N-1,0]
,但这么简单就是写错了,WA了还以为要多巧妙的构造,就是没仔细检查自己的代码。问了新同学,问问题的过程中发现了错误。不过,因此认识了很多人:该同学、老师、其他同学,一切好像是有缘分在的。
1
2
3
4
5
6
7
8
9
10
11
|
#include <iostream>
using namespace std;
const int N = 1e5 + 5;
int main()
{
cout << N << endl;
for (int i = N; i >= 1; i--)
cout << i << " ";
cout << endl;
return 0;
}
|
D. 合并果子
最小堆的应用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
int main()
{
int n;
cin >> n;
int num;
priority_queue<int,vector<int>,greater<int>> q;
for(int i=0;i<n;i++){
cin >> num;
q.push(num);
}
int res=0;
while(q.size()>=2){
int a=q.top();
q.pop();
int b=q.top();
q.pop();
q.push(a+b);
res+=a+b;
}
cout << res;
return 0;
}
|
E. 差分计数
1、这题要特别注意输入数值范围:
x和a都是[-2*10^6,2*10^6]
用map会超时,因为底层是红黑树,插入是O(nlogn)。所以开数组
作哈希表,但是输入的整数x可能为负数,可以加一个大数
将数整体右移
映射,因此数组至少开原来范围的2倍以上。
2、0的特殊情况。
因为这里是有序对 (i,j) 个数
。没有说下标i和j的大小关系,当x为0时,即可能有i=j i>j i<j
共3种情况。
例如:
input:
2 0
1 1
output:
4
有序对为: (0,0) (0,1) (1,0) (1,1)
input:
4 0
1 1 1 1
output:
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int M=3e6;
const int N=6e6;
static int read() {
int x = 0, w = 1;
char c = 0;
while (c < '0' || c > '9') {
if (c == '-') w = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
x = x * 10 + (c - '0');
c = getchar();
}
return x * w;
}
int main()
{
int n=read();
int x=read();
x=abs(x);
vector<long long> a(N,0);
long long res=0;
for(int i=0;i<n;i++){
int num=read();
a[num+M]+=1;
}
if(x==0){
for(int i=0;i<N;i++){
if(a[i]==0) continue;
res+=a[i]*a[i];
}
}else{
for(int i=0;i<N-x;i++){
res+=a[i]*a[i+x];
}
}
printf("%lld",res);
return 0;
}
|
F.制作菜肴
做题时自己写的test case:
input:
4 4
2 3 4 5
4 2 1 3
3 4 5 2
1 2 3 4
output:
0 0 0 0
0 150 240 0
0 80 160 0
0 0 0 0
很容易看懂的一道题,但刚开始使用什么数据结构如何实现这个过程还是修修改改了几遍,最终思路清晰起来,只要从4个方向遍历即可。写出来下面这个版本,因为数据存储使用了int一直WA,也是请教了同学后改为long才AC的。
算是刷LeetCode刷出来的解法:为了不发生越界错误,在四周多加了一层数据-1
便于比较大小,因为T是在[1,10^4]之间
。
看了那位同学的代码,发现厉害的人的确厉害,思维严谨。他为了越界错误,是分情况讨论的,但他在前一晚很短时间内就完成代码AC了。虽然一直在劝自己对高学历祛魅,高学历!=一切,但是值得从身边不同的人学习。比如这种严谨
(真的很想强调这个严谨,目前认识人里最严谨的了,之前认识常春藤本硕的都没这么严谨)和认真。本来就和STJU的基础有差,然后别人又都在很拼,如果我再不努力,日积月累别说缩小差距了,只会越来越大。
P.S. 回去翻聊天记录真的想骂死自己😅,总算是知道了有些人的聊天记录是应该多看几遍的。自己还在这振振有词地总结自己的错误,殊不知人家早就给出了答案(看聊天记录的时间就知道了)。还WA了好几遍发了好几次代码截图过去又请教,果然基础差的我是无知的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
int n,m;
scanf("%d %d",&n,&m);
vector<vector<long>> v(n+1,vector<long>(m+1,-1));
vector<vector<long>> top_left(n+2,vector<long>(m+2,-1)),
bottom_left(n+2,vector<long>(m+2,-1)),
top_right(n+2,vector<long>(m+2,-1)),
bottom_right(n+2,vector<long>(m+2,-1));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%ld",&v[i][j]);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
top_left[i][j]=max({top_left[i-1][j],top_left[i][j-1],v[i][j]});
}
}
for(int i=1;i<=n;i++){
for(int j=m;j>=1;j--){
top_right[i][j]=max({top_right[i-1][j],top_right[i][j+1],v[i][j]});
}
}
for(int i=n;i>=1;i--){
for(int j=1;j<=m;j++){
bottom_left[i][j]=max({bottom_left[i][j-1],bottom_left[i+1][j],v[i][j]});
}
}
for(int i=n;i>=1;i--){
for(int j=m;j>=1;j--){
bottom_right[i][j]=max({bottom_right[i+1][j],bottom_right[i][j+1],v[i][j]});
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(j==m){
printf("0");
}else if(i==1 || j==1 || i==n){
printf("0 ");
}else{
cout << top_left[i-1][j-1]*
top_right[i-1][j+1]*
bottom_left[i+1][j-1]*
bottom_right[i+1][j+1] << " ";
}
}
printf("\n");
}
return 0;
}
|
G. 箱子打包
贪心算法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
int n,l;
cin >> n >> l;
vector<int> v(n);
for(int i=0;i<n;i++){
cin >> v[i];
}
sort(v.begin(),v.end(),[](int a,int b){
return a>b;
});
int left=0,right=n-1;
int res=0;
while(left<=right){
if(v[left]+v[right]<=l){
res+=1;
left++;
right--;
}else{
res+=1;
left++;
}
}
cout << res;
return 0;
}
|
- 时间复杂度:O(nlogn)
- 空间复杂度:O(nlogn)
H. 大鱼吃小鱼
这题重点在于吃鱼顺序,即排序方法。
1
2
3
4
5
6
7
8
9
10
11
12
|
bool cmp(node x,node y){
if(x.a>0 && y.a>0){
//positive w小
return x.w<y.w;
}else if(x.a<0 && y.a<0){
//negative w+a大
return x.w+x.a>y.w+y.a;
}else{
//start from positive a to negative a
return x.a>y.a;
}
}
|
w+a排序举例:
[(7.-2), (8,-6)],此时先吃7(需要初值10)比先吃8(需要初值13)更好。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
#include <iostream>
#include <vector>
#include <algorithm>
#include <limits.h>
#include <unordered_set>
using namespace std;
struct node{
long long w,a;
};
bool cmp(node x,node y){
if(x.a>0 && y.a>0){
//positive w小
return x.w<y.w;
}else if(x.a<0 && y.a<0){
//negative w+a大
return x.w+x.a>y.w+y.a;
}else{
//start from positive a to negative a
return x.a>y.a;
}
}
int main()
{
long long n;
scanf("%lld",&n);
vector<node> wa(n);
for(int i=0;i<n;i++){
scanf("%lld %lld",&wa[i].w,&wa[i].a);
}
sort(wa.begin(),wa.end(),cmp);
// for(int i=0;i<n;i++){
// cout << wa[i].w << " " << wa[i].a << endl;
// }
long long res=1;
long long sum=0;
long long size=1;
for(int i=0;i<n;i++){
//任何时刻都要保证鱼的大小是正整数。
if(size>wa[i].w){
sum=0;
size+=wa[i].a;
}else{
sum=wa[i].w-size;
size=wa[i].w+wa[i].a;
}
if(size<=0){
sum+=(1-size);
size=1;
}
res+=sum;
}
printf("%lld",res);
return 0;
}
|
- 时间复杂度:O(nlogn)
- 空间复杂度:O(nlogn)
I. 离线缓存
debug了好多次,每次好几个小时的一道题。之前学OS的时候没发现这个知道未来情况的算法有多难,这道题倒是体会了。
其实每道题都应该:先有一个准确思路——>根据思路写代码——>完善细节。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
#include <iostream>
#include <queue>
#include <map>
#include <set>
using namespace std;
map<int, int> mp, flag, nxt;
int main()
{
int n, k;
scanf("%d %d", &n, &k);
vector<int> v(n+1);
for (int i = 1; i <= n; i++)
{
scanf("%d", &v[i]);
}
for (int i = n; i >= 1; i--)
{
if (mp.count(v[i]))
{
nxt[i] = mp[v[i]];
}
else
{
nxt[i] = n + 1;
}
mp[v[i]] = i;
}
set<pair<int, int> > cache;
int res = 0;
int cnt = 0;
for (int i = 1; i <= n; i++)
{
if (flag[v[i]])
{
// found, update
auto it = cache.lower_bound(make_pair(i, v[i]));
auto temp = *it;
cache.erase(it);
temp.first = nxt[i];
cache.insert(temp);
continue;
}
flag[v[i]] = 1;
cnt++;
res++;
cache.insert(make_pair(nxt[i], v[i]));
if (cnt > k)
{
cnt--;
flag[(--cache.end())->second] = 0;
cache.erase(--cache.end());
}
}
printf("%d", res);
return 0;
}
|
J. 糖果
brute-force
60'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
#include <iostream>
#include <algorithm>
#include<vector>
using namespace std;
//a-b:x
//60‘
const int N=1e5+5;
struct node{
int x,a,b;
}v[N];
int res[N];
int main()
{
int n,k;
scanf("%d%d",&n,&k);
int x,a,b;
//20' runtime error: SIGSEGV
// vector<node> v(n+1);
for(int i=1;i<=k;i++){
scanf("%d%d%d",&v[i].x,&v[i].a,&v[i].b);
}
// vector<int> res(n+1,1);
//greedy
for(int i=1;i<=n;i++){
res[i]=1;
}
for(int k=0;k<250;k++){
for(int i=1;i<=k;i++){
switch(v[i].x){
case 1:
if(res[v[i].a]>res[v[i].b]) res[v[i].b]=res[v[i].a];
else res[v[i].a]=res[v[i].b];
break;
case 2:
if(res[v[i].a]>=res[v[i].b]) res[v[i].b]=res[v[i].a]+1;
break;
case 3:
if(res[v[i].a]<res[v[i].b]) res[v[i].a]=res[v[i].b];
break;
case 4:
if(res[v[i].a]<=res[v[i].b]) res[v[i].a]=res[v[i].b]+1;
break;
case 5:
if(res[v[i].a]>res[v[i].b]) res[v[i].b]=res[v[i].a];
break;
}
}
}
for(int i=1;i<=k;i++){
if(v[i].x==1){
if(res[v[i].a]>res[v[i].b]){
cout << "-1";
return 0;
}
}else if(v[i].x==2){
if(res[v[i].a]>=res[v[i].b]){
cout << "-1";
return 0;
}
}else if(v[i].x==3){
if(res[v[i].a]<res[v[i].b]){
cout << "-1";
return 0;
}
}else if(v[i].x==4){
if(res[v[i].a]<=res[v[i].b]){
cout << "-1";
return 0;
}
}else if(v[i].x==5){
if(res[v[i].a]>res[v[i].b]){
cout << "-1";
return 0;
}
}
}
long long ans=0;
for(int i=1;i<=n;i++){
ans+=res[i];
}
cout << ans;
return 0;
}
|
SPFA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
#include <iostream>
#include <algorithm>
#include<vector>
#include<queue>
using namespace std;
const int N=1e7+5;
struct node{
int from,to;
int w;
int next;
}v[N];
int head[N],vis[N],dis[N],tot[N];
queue<int> q;
int n,k;
int t;
void add(int a,int b,int x){
v[++t].from=a;
v[t].to=b;
v[t].w=x;
v[t].next=head[a];
head[a]=t;
}
void SPFA(){
vis[0]=1;
q.push(0);
while(!q.empty()){
int cur=q.front();
q.pop();
vis[cur]=0;
if(tot[cur]==n-1){
cout << -1;
exit(0);
}
tot[cur]++;
for(int i=head[cur];i;i=v[i].next){
int temp=v[i].to;
if(dis[temp]<dis[cur]+v[i].w){
dis[temp]=dis[cur]+v[i].w;
if(!vis[temp]){
vis[temp]=1;
q.push(temp);
}
}
}
}
}
int main()
{
scanf("%d%d",&n,&k);
int x,a,b;
for(int i=1;i<=k;i++){
scanf("%d%d%d",&x,&a,&b);
if(x==1){
add(a,b,0);
add(b,a,0);
}else if(x==2){
if(a==b){
cout << -1;
return 0;
}
add(a,b,1);
}else if(x==3){
add(b,a,0);
}else if(x==4){
if(a==b){
cout << -1;
return 0;
}
add(b,a,1);
}else if(x==5){
add(a,b,0);
}
}
for(int i=n;i>=1;i--){
add(0,i,1);
}
SPFA();
long long res=0;
for(int i=1;i<=n;i++){
res+=dis[i];
}
cout << res;
return 0;
}
|
K. 工具人
注意:
n: [1,10^6]
a: [0,10^9] 所以刚开始是没有负值的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
long long res,avg;
int main()
{
int n;
scanf("%d",&n);
vector<long long> a(n),b(n);
for(int i=0;i<n;i++){
scanf("%lld",&a[i]);
avg+=a[i];
}
if(avg%n!=0){
printf("-1");
return 0;
}
avg/=n;
for(int i=0;i<n;i++){
b[i]=a[i]-avg;
}
long long neg_max=0,pos_max=0,sum=0;
for(int i=0;i<n;i++){
sum+=b[i];
if(sum<0 && sum<neg_max){
neg_max=sum;
}
if(sum>0 && sum>pos_max){
pos_max=sum;
}
}
printf("%lld",abs(neg_max-pos_max));
return 0;
}
|
举例:
input:
4
4 0 0 0
avg:1
b[i]=a[i]-avg: 3 -1 -1 -1
sum: 3 2 1 0
neg_max: 0
pos_max: 3
res:3
转换过程:
4 0 0 0
3 1 0 0
2 1 1 0
1 1 1 1
output: 3
input:
4
4 0 0 4
avg:2
b[i]=a[i]-avg: 2 -2 -2 2
sum: 2 0 -2 0
neg_max: -2
pos_max: 2
res:4
L. 数列极差
优先队列(最大堆、最小堆)的应用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
#include <iostream>
#include <queue>
#include <algorithm>
#include <queue>
using namespace std;
int main()
{
int n;
cin >> n;
priority_queue<int,vector<int>,greater<int>> minHeap;
priority_queue<int> maxHeap;
int num;
for(int i=0;i<n;i++){
cin >> num;
minHeap.push(num);
maxHeap.push(num);
}
cin >> num;
int m;
int a,b;
for(int i=0;i<n-1;i++){
a=minHeap.top();
minHeap.pop();
b=minHeap.top();
minHeap.pop();
minHeap.push(a*b+1);
a=maxHeap.top();
maxHeap.pop();
b=maxHeap.top();
maxHeap.pop();
maxHeap.push(a*b+1);
}
cout << abs(maxHeap.top()-minHeap.top());
return 0;
}
|
发现对别人轻而易举很容易的事情,自己总要花很多时间掌握,总会解决的不是吗?也会后悔当时复试前摆烂没有好好刷ECNU OJ平台的题,在想当时努力刷题这次算法课练习会不会容易一些。真的好几道题花了好几次尝试解决,每次都是2小时起步,有天周日debug了一下午“离线缓存”也没有AC,导致那天心情特别低沉。想起之前看过的“当下的你逃避学习的时候,或许是未来的你在向你求救。”
,每一份努力的确不会被辜负。清楚发现自己花的时间远远不够,所以真的再用心多动脑。任何困难也都会过去的。Never complain.
Author
Anjana
LastMod
2022-11-13
License
原创文章,如需转载请注明作者和出处。谢谢!