双十中学2020 暑假模拟测试 Round #4(div 2)全题解
灌溉(irrigation)
思路——最小生成树
一看这题目,明显的最小生成树模板题。
给每两个点之间建边,边权是它们的欧氏距离,当欧式距离 < C的时候边权为无穷大,然后直接上Prim模板就行了。因为我只会写Prim
代码
#include <bits/stdc++.h>
#define ri register int
using namespace std;
const int INF = 2147483647;
const int maxn = 2e3 + 7;
int n,c,x[maxn],y[maxn],mmin,u,ans;
int g[maxn][maxn],d[maxn],vis[maxn];
inline int sqr(const int &x) {
return x * x;
}
int main() {
scanf("%d%d",&n,&c);
for(int i = 1;i <= n;++ i) {
scanf("%d%d",&x[i],&y[i]);
}
for(ri i = 1;i <= n;++ i) { //建边
for(ri j = 1;j <= n;++ j) {
ri t = sqr(x[i] - x[j]) + sqr(y[i] - y[j]);
g[i][j] = (t < c ? INF : t);
}
}
//以下是Prim算法
memset(vis,true,sizeof(vis));
vis[1] = false;
for(ri i = 1;i <= n;++ i) {
d[i] = g[1][i];
}
for(ri i = 1;i < n;++ i) {
mmin = INF,u = 0;
for(ri j = 1;j <= n;++ j) {
if(d[j] < mmin && vis[j]) {
mmin = d[j],
u = j;
}
}
if(mmin == INF) {
printf("-1");
return 0;
}
ans += mmin;
vis[u] = 0;
for(ri j = 1;j <= n;++ j) {
if(vis[j] && g[u][j] < d[j]) {
d[j] = g[u][j];
}
}
}
//不会有人认为以下也是Prim算法吧。。。。。。
printf("%d\n",ans);
return 0;
}
懒惰的奶牛(lazy) 魔改版本
解题思路——前缀和
原题也是用的前缀和 原题题解我还没写
虽然被魔改后加了一维,但也丝毫不影响我用前缀和。
我们对每一行做一个前缀和,然后枚举每个位置。
假如那个位置的坐标是(i,j),储存前缀和的数组为b;
那么这个位置能吃到的牧草的数量就是:
∑
l
i
n
e
=
i
−
k
i
+
k
b
[
l
i
n
e
]
[
j
+
k
−
∣
i
−
l
i
n
e
∣
]
−
b
[
l
i
n
e
]
[
i
−
k
+
∣
i
−
l
i
n
e
∣
]
\sum_{line=i-k}^{i+k}b[line][j+k-|i - line|] - b[line][i-k+|i - line|]
∑line=i−ki+kb[line][j+k−∣i−line∣]−b[line][i−k+∣i−line∣]
求出每个位置能吃到的牧草,然后取其中的最大值就能得出答案。
答案
#include <bits/stdc++.h>
#define ri register int
using namespace std;
const int maxn = 407;
int n,k,a[maxn][maxn],b[maxn][maxn];
int up,down,s,l,r,sum,ans;
int main() {
scanf("%d%d",&n,&k);
for(ri i = 1;i <= n;++ i) {
for(ri j = 1;j <= n;++ j) {
scanf("%d",&a[i][j]);
b[i][j] = b[i][j - 1] + a[i][j];
}
}
for(ri i = 1;i <= n;++ i) {
for(ri j = 1;j <= n;++ j) {
up = i + k,down = i - k,sum = 0;
if(up > n) up = n;
if(down < 1) down = 1;
for(ri li = down;li <= up;++ li) {
s = abs(i - li);
l = max(j - k + s,1);
r = min(j + k - s,n);
sum += b[li][r] - b[li][l - 1];
}
ans = max(ans,sum);
}
}
printf("%d\n",ans);
return 0;
}
哞哞(mooomoo)
解题思路——背包
看题目描述就有背包的影子,果断抄起背包 就打 就写
水题没什么好说的,要是搞不懂那就看代码
代码
#include <bits/stdc++.h>
using namespace std;
const int maxs = 1e6 + 7;
const int maxn = 107;
const int maxm = 27;
int f[maxs];
int a[maxn],b[maxn],v[maxm];
int n,m,ms;
int main() {
scanf("%d%d",&n,&m);
for(int i = 1;i <= m;++ i) {
scanf("%d",&v[i]);
}
for(int i = 1;i <= n;++ i) {
scanf("%d",&a[i]);
}
for(int i = 1;i <= n;++ i) {
b[i] = a[i] - (max(a[i - 1] - 1,0));
ms = max(ms,b[i]);
}
memset(f,0x3f,sizeof(f));
f[0] = 0;
for(int i = 1;i <= m;++ i) { //背包
for(int j = v[i];j <= ms;++ j) {
f[j] = min(f[j],f[j - v[i]] + 1);
}
}
int ans = 0;
for(int i = 1;i <= n;i ++) {
if(f[b[i]] == 0x3f3f3f3f) {
printf("-1\n");
return 0;
}
ans += f[b[i]];
}
printf("%d\n",ans);
}
}
奶牛选美大赛(beauty)
解题思路——DFS + 魔改Floyd
三个块相连有两种方式:
方式1:
三个块直接相连,比如:
块1连块2,块2连块3;
块1连块3,块2连块3;
块1连块3,块1连块2;
方式2:
三个块都连在某一个点上
算法大致
输入 O(n2)
首先先用DFS标记出每一个块 O(2n2)
所以我们要求出每一个点到每一个块的最短距离 O(n4)
接着求出相连方式1的最短值 O(n2)
最后利用魔改Floyd求出相连方式2的最短值 O(n2)
总时间 O(n4+5n2), n ≤ 50 n \leq 50 n≤50 完全够用
#include <bits/stdc++.h>
using namespace std;
const int maxn = 57;
const int dx[4] = {0,0,1,-1};
const int dy[4] = {1,-1,0,0};
bool vis[maxn][maxn];
char s[maxn][maxn];
int n,m,cnt,p[maxn][maxn];
int dis[4][maxn][maxn];
int f[maxn][maxn];
inline void dfs(int x,int y) {
if(vis[x][y]) {
return ;
}
if(s[x][y] == 'X') {
vis[x][y] = 1;
p[x][y] = cnt;
for(int i = 0;i < 4;++ i) {
dfs(x + dx[i],y + dy[i]);
}
}
}
inline void distance(int x,int y,int id) {
for(int i = 1;i <= n;++ i){
for(int j = 1;j <= m;++ j) {
dis[id][i][j] = min(dis[id][i][j],abs(x - i) + abs(y - j));
}
}
}
int main() {
memset(dis,0x3f,sizeof(dis));
memset(f,0x3f,sizeof(f));
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;++ i) {
scanf("%s",s[i] + 1);
}
for(int i = 1;i <= n;++ i) {
for(int j = 1;j <= m;++ j) {
if(!vis[i][j] && s[i][j] == 'X') {
++ cnt;
dfs(i,j);
}
}
}
for(int i = 1;i <= n;++ i) {
for(int j = 1;j <= m;++ j) {
if(p[i][j] != 0) {
distance(i,j,p[i][j]);
}
}
}
for(int i = 1;i <= n;++ i) {
for(int j = 1;j <= m;++ j) {
if(p[i][j] != 0) {
f[p[i][j]][1] = f[1][p[i][j]] = min(f[1][p[i][j]],dis[1][i][j]);
f[p[i][j]][2] = f[2][p[i][j]] = min(f[2][p[i][j]],dis[2][i][j]);
f[p[i][j]][3] = f[3][p[i][j]] = min(f[3][p[i][j]],dis[3][i][j]);
}
}
}
int ans = min(f[1][2] + f[2][3],min(f[1][3] + f[2][3],f[1][2] + f[1][3]));
for(int i = 1;i <= n;++ i) {
for(int j = 1;j <= m;++ j) {
ans = min(ans,dis[1][i][j] + dis[2][i][j] + dis[3][i][j]);
}
}
cout << ans - 2;
return 0;
}
如果以上代码或思路上有什么问题的欢迎在评论区指出 本蒟蒻保证不了能否回答
*ps:
此题解是本人闲的无聊才写的 写题解真累
至于Round #1 #2 #3……以后有时间再写吧……

本文详细解析了双十中学2020年暑假模拟测试的四道算法题,包括最小生成树、前缀和、背包问题及DFS+Floyd算法的应用。
794

被折叠的 条评论
为什么被折叠?



