天天看点

2020第十一届软件类省内模拟赛(非官方)1. 字节计算2. 无向连通图3. 蓝桥单词(组合)4. 合法括号序列(dfs)5. 单词加密(模拟)6. 反倍数(枚举)7. 螺旋矩阵(枚举 + 标记)8. 摆动序列(dp)9. 郊外植树(dfs + 剪枝)10. 村庄建设(mst)

1. 字节计算

  思路 :直接计算器计算即可。

//在计算机存储中,12.5MB是多少字节?
#include<bits/stdc++.h>
using namespace std;

int main() {
	cout << 13107200 << endl;
	return 0;
} 
           

2. 无向连通图

  思路 :直接计算即可。

//一个包含有2019个结点的无向连通图,最少包含多少条边?
#include<bits/stdc++.h>
using namespace std;

int main() {
	cout << 2018 << endl;
	return 0;
} 
           

3. 蓝桥单词(组合)

  思路 :高中数学,首先对字母进行全排列 A 7 7 A^{7}_{7} A77​,但是由于有两个重复字母 A 7 7 / A 2 2 A^{7}_{7} / A^{2}_{2} A77​/A22​

#include<bits/stdc++.h>
using namespace std;

int main() {
	cout << 2520 << endl;
	return 0;
} 
           

4. 合法括号序列(dfs)

  思路 :直接全排列判断有多少符合题意。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

bool check (string str) {
    int n = str.length();
    stack<char> s;
    for (int i = 0; i < n; ++ i) {
        if (str[i] == '(') s.push(str[i]);
        else {
            if (s.size()) {
                if (s.top() == '(') s.pop();
            } else s.push(str[i]);
        }
    }
    return s.size();
}

int main () {
    string str = "(((())))";
    int ans = 0;
    do {
        //cout << str << endl;
        if (!check(str)) ++ ans;
    } while (next_permutation(str.begin(), str.end()));
    cout << ans << endl;
    return 0;
}
           

5. 单词加密(模拟)

  思路 :直接按照题意枚举即可。

//解密
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 1e3 + 10;
char str[maxn];

int main() {
	scanf("%s", str);
	int n = strlen(str);
	for (int i = 0; i < n; ++ i) {
		if (str[i] == 'x') str[i] = 'a';
		else if (str[i] == 'y') str[i] = 'b';
		else if (str[i] == 'z') str[i] = 'c';
		else str[i] = str[i] + 3;
	} 
	//puts(str);
	for (int i = 0; i < n; ++ i) printf("%c", str[i]);
	printf("\n");
	return 0;
}
           

6. 反倍数(枚举)

  思路 :直接遍历给出的数判断计数即可。

//反倍数
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 1e6 + 10;

int main() {
	int n; scanf("%d", &n);
	int a, b, c; scanf("%d%d%d", &a, &b, &c);
	int cnt = 0;
	for (int i = 1; i <= n; ++ i) {
		if (i % a != 0 && i % b != 0 && i % c != 0) {
			//cout << i << " ";
			cnt ++;
		}
	}
	printf("%d\n", cnt);
	return 0;
}
           

7. 螺旋矩阵(枚举 + 标记)

  思路 :首先对矩阵进行一个标记,然后从第一个格子开始进行填数,填过数的格子标记为 f a l s e false false,直到所有的格子都标记完毕即可。

//矩阵
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 1e3 + 10;
int mat[maxn][maxn];
bool vis[maxn][maxn];

int main() {
	int n, m; scanf("%d%d", &n, &m);
	int r, c; scanf("%d%d", &r, &c);
	for (int i = 1; i <= n; ++ i) {
		for (int j = 1; j <= m; ++ j) vis[i][j] = false;
	}
	int cnt = 1, x = 1, y = 0;
	while (cnt <= n * m) {
		while (!vis[x][y + 1]) {
			mat[x][++ y] = cnt ++;
			vis[x][y] = true;
			if (y == m) break;
		}
		while (!vis[x + 1][y]) {
			mat[++ x][y] = cnt ++;
			vis[x][y] = true;
			if (x == n) break;
		}
		while (!vis[x][y - 1]) {
			mat[x][-- y] = cnt ++;
			vis[x][y] = true;
			if (y == 1) break;
		}
		while (!vis[x - 1][y]) {
			mat[-- x][y] = cnt ++;
			vis[x][y] = true;
			if (x == 1) break;
		}
	}
	/*for (int i = 1; i <= n; ++ i) {
		for(int j = 1; j <= m; ++ j) cout << mat[i][j] << " ";
		cout << endl;
	}*/
	printf("%d\n", mat[r][c]);
	return 0;
}
           

8. 摆动序列(dp)

  • 50% 数据

      思路 :直接按照题意 d f s dfs dfs 即可。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int mod = 10000;
const int maxn = 1e3 + 10;
int ans = 0, m, n;

void dfs(int now, int cnt) {
	if (cnt == m) {
		ans = (ans + 1) % mod;
		return ;
	} 
	for (int i = 1; i <= n; ++ i) {
		if (!cnt) dfs(i, cnt + 1);
		else {
			if (cnt & 1 && i < now) dfs(i, cnt + 1);
			if (!(cnt & 1) && i > now) dfs(i, cnt + 1);
		}
	}
}

int main() {
	scanf("%d%d", &m, &n);
	dfs(-1, 0);
	printf("%d\n", ans);
	return 0;
}
           
  • 80% 数据

      思路 :奇数项都比前一项大,偶数项都比前一项小,设 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示第 i i i 个数字,选择 j j j 时一共有多少种。当当前的数是奇数项的时候,那么前面一项小于 j j j 的和可以推出当前项的种数,如果当前数是偶数项的时候,那么前面一项大于 j j j 的状态的和可以推出当前项,也就是(记得最终结果取模)

    d p [ i ] [ j ] + = d p [ i − 1 ] [ k ] , k ∈ [ 1 , j ) , i % 2 = = 1 dp[i][j] += dp[i - 1][k], k ∈[1, j), i\%2==1 dp[i][j]+=dp[i−1][k],k∈[1,j),i%2==1

    d p [ i ] [ j ] + = d p [ i − 1 ] [ k ] , k ∈ [ j + 1 , n ] , i % 2 = = 0 dp[i][j] += dp[i - 1][k], k ∈[j + 1, n], i\%2==0 dp[i][j]+=dp[i−1][k],k∈[j+1,n],i%2==0

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 1e3 + 10;
const int mod = 10000;
int dp[maxn][maxn];//第i个数字选择j的种数

//奇数项都比前一项大,偶数项都比前一项小,种数
int main () {
    int m, n;
    scanf("%d%d", &m, &n);
    for (int i = 0; i <= m; ++ i) {
        for (int j = 0; j <= n; ++ j) {
            dp[i][j] = 0;
        }
    }
    for(int i = 1; i <= n; ++ i) dp[1][i] = 1;
    for (int i = 2; i <= m; ++ i) {
        if (i & 1) {
            for (int j = 1; j <= n; ++ j) {
                for (int k = 1; k < j; ++ k) {
                    dp[i][j] += dp[i - 1][k];
                    dp[i][j] %= mod;
                }
            }
        } else {
            for (int j = 1; j <= n; ++ j) {
                for (int k = j + 1; k <= n; ++ k) {
                    dp[i][j] += dp[i - 1][k];
                    dp[i][j] %= mod;
                }
            }
        }
    }
    /*for (int i = 1; i <= m; ++ i) {
        for (int j = 1; j <= n; ++ j) cout << dp[i][j] << " ";
        cout << endl;
    }*/
    int ans = 0;
    for (int i = 1; i <= n; ++ i) ans = (ans + dp[m][i]) % mod;
    printf("%d\n", ans);
    return 0;
}
           
  • 100% 数据

      思路 :可以对上面的程序进行数据的输出,当前行 i i i 的值的总和表示长度为 i i i 的摆动序列一共有多少种。如果上面的程序做一个前缀和的操作就可以对上面程序作出一个改进,我们对上面的程序进行改进,当 i = = 1 i == 1 i==1 的时候, d p [ i ] [ j ] dp[i][j] dp[i][j] 表示第 i i i 个数大于等于 j j j 的时候的种数,这时如果当前项是偶数项的时候,它的前面一项是奇数项,我们需要使用前面一项大于等于 j j j 的结果来推算出当前的结果,在加上使用前缀和优化,就有 d p [ i ] [ j ] = d p [ i − 1 ] [ j + 1 ] + d p [ i ] [ j − 1 ] dp[i][j] = dp[i - 1][j + 1] + dp[i][j - 1] dp[i][j]=dp[i−1][j+1]+dp[i][j−1],当前项是奇数项的时候我们可以使用前面一项,也就是偶数项的小于等于 j − 1 j - 1 j−1 的这种状态得到,所以有 dp[i][j] = (dp[i - 1][j - 1] + dp[i][j + 1])(从 80% 的程序中输出结果矩阵,可以发现一些规律,奇数行都是从大到小,偶数行都是从小到大。)

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 1e3 + 10;
const int mod = 10000;
int dp[maxn][maxn];

//奇数项都比前一项大,偶数项都比前一项小,种数
int main () {
    int m, n;
    scanf("%d%d", &m, &n);
    for (int i = 0; i <= m; ++ i) {
        for (int j = 0; j <= n; ++ j) {
            dp[i][j] = 0;
        }
    }
    for(int i = 1; i <= n; ++ i) dp[1][i] = n - i + 1; //大于等于i的数量
    for (int i = 2; i <= m; ++ i) {
        if (i & 1) {
            /*for (int j = 1; j <= n; ++ j) {
                for (int k = 1; k < j; ++ k) {
                    dp[i][j] += dp[i - 1][k];
                    dp[i][j] %= mod;
                }
            }*/
            //前面一行是偶数行<=j-1
            for (int j = n; j >= 1; -- j) dp[i][j] = (dp[i - 1][j - 1] + dp[i][j + 1]) % mod;
        } else {
            /*for (int j = 1; j <= n; ++ j) {
                for (int k = j + 1; k <= n; ++ k) {
                    dp[i][j] += dp[i - 1][k];
                    dp[i][j] %= mod;
                }
            }*/
            //前面一行是奇数行>=j+1
            for (int j = 1; j <= n; ++ j) dp[i][j] = (dp[i - 1][j + 1] + dp[i][j - 1]) % mod;
        }
    }
    int ans = (m & 1) ? dp[m][1] : dp[m][n];
    //优化代码中这里变成加上一个前缀和
    //for (int i = 1; i <= n; ++ i) ans = (ans + dp[m][i]) % mod;
    printf("%d\n", ans);
    return 0;
}
           

9. 郊外植树(dfs + 剪枝)

  思路 :由于数据范围比较小,我们可以直接 d f s dfs dfs,此题的优化点在于在判断当前选择的圆是否和之前已经选择好的圆冲突。设置一个估价函数来进行剪枝,对圆按照半径从大到小排序之后,我们记录下从所有圆的面积的后缀和,在我们搜索的时候如果当前的面积加上后缀的面积还没有已经得到的面积大那么就进行返回,同时我们使用了一个二进制的数来记录了每个圆的选择状态,当我们选择当前的圆的时候首先判断是否和已经选择过的圆冲突,如果冲突则不选择当前圆,如果不冲突则选择当前圆。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 40;
bool vis[maxn][maxn];
int suf[maxn];

struct Node {
    int x, y, r;
    bool operator < (const Node & xxx) const {
        return r > xxx.r;
    }
}node[maxn];

int n, ans = 0;
map<int, int> m;

int dis2(int i, int j) {
    return (node[i].x - node[j].x) * (node[i].x - node[j].x) + (node[i].y - node[j].y) * (node[i].y - node[j].y);
}

void dfs(int now, int sum, int status) {
    if (now > n) {
        ans = max(ans, sum);
        return ;
    }
    if (ans > sum + suf[now]) return ;
    bool flag = true;
    int j = 0;
    for (int i = status; i; i -= j) {
        j = i & (-i);
        if (vis[now][m[j]]) flag = false;
        if (!flag) break;
    }
    if (flag) dfs(now + 1, sum + node[now].r * node[now].r, status | (1 << now));
    dfs(now + 1, sum, status);
}

int main () {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++ i) for (int j = 1; j <= n; ++ j) vis[i][j] = false;
    /*for (int i = 1; i <= n; ++ i) {
        for (int j = 1; j <= n; ++ j) cout << vis[i][j] << " ";
        cout << endl;
    }*/
    for (int i = 1; i <= n; ++ i) {
        scanf("%d %d %d", &node[i].x, &node[i].y, &node[i].r);
        m[1 << i] = i;
    }
    sort(node + 1, node + n + 1);
    for (int i = 1; i <= n; ++ i) {
        for (int j = 1; j <= n; ++ j) {
            if (dis2(i, j) < (node[i].r + node[j].r) * (node[i].r + node[j].r)) vis[i][j] = true;
        }
    }
    /*for (int i = 1; i <= n; ++ i) {
        for (int j = 1; j <= n; ++ j) cout << vis[i][j] << " ";
        cout << endl;
    }*/
    for (int i = n; i >= 1; -- i) suf[i] = suf[i + 1] + node[i].r * node[i].r;
    dfs(1, 0, 0);
    printf("%d\n", ans);
    return 0;
}
           

10. 村庄建设(mst)

  思路 :MST 裸题,注意数据类型即可。

//植树
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 1e3 + 10;
int f[maxn];

struct Node {
	int x, y, h, num;
}node[maxn];

struct Edge {
	int u, v;
	double cost;
	bool operator < (const Edge & xxx) const {
		return cost < xxx.cost;
	}
}edge[maxn * maxn];

double dis(Node a, Node b) {
	return sqrt(1ll * (a.x - b.x) * (a.x - b.x) + 1ll * (a.y - b.y) * (a.y - b.y)) + 1ll * (a.h - b.h) * (a.h - b.h) * 1.0;
}

int Find(int v) {
	if (v == f[v]) return v;
	else return f[v] = Find(f[v]);	
}

bool Merge(int u, int v) {
	int t1 = Find(u), t2 = Find(v);
	if (t1 != t2) {
		f[t1] = t2;
		return true;
	}
	return false;
}

int main () {
	int n; scanf("%d", &n);
	for (int i = 1; i <= n; ++ i) f[i] = i;
	for (int i = 1; i <= n; ++ i) {
		scanf("%d%d%d", &node[i].x, &node[i].y, &node[i].h);
		node[i].num = i;
	}
	int cnt = 0;
	for (int i = 1; i <= n; ++ i) {
		for (int j = i + 1; j <= n; ++ j) {
			edge[cnt].u = node[i].num;
			edge[cnt].v = node[j].num;
			edge[cnt ++].cost = dis(node[i], node[j]);
		}
	}
	sort(edge, edge + cnt);
	double ans = 0;
	int cntEdge = 0;
	for (int i = 0; i < cnt; ++ i) {
		if (Merge(edge[i].u, edge[i].v)) {
			++ cntEdge; ans += edge[i].cost;
			if (cntEdge == n - 1) break;
		}
	}
	printf("%.2f\n", ans);
	return 0;
} 
           

继续阅读