总体上看,感觉今年的省赛题比去年的难,下面是我的个人题解,大佬勿喷
真题链接:https://www.dotcpp.com/oj/train/1026/
A. 九进制转十进制
这道是送分题了,2*(9**3) + 2*9 + 2 = 1478
B. 顺子日期
这道题有歧义,鬼知道012 210 321算不算,我填了4种
C. 刷题统计
先算出需要多少个星期,再算一下还要多多少天
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
ll a,b,n;
cin >> a >> b >> n;
ll week = a * 5 + b * 2;
ll m = n / week * 7;
ll mm = n / week * week;
int i = 0;
while(mm < n) {
if(i <= 4) {
mm += a;
} else {
mm += b;
}
++i;
++m;
}
cout << m << endl;
return 0;
}
运行结果:
D. 修剪灌木
这题换句话说就是从这个位置出发,向左走再走回来和向右走再走回来,哪个距离大
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
int n;
cin >> n;
for(int i = 0; i < n; ++i) {
cout << max(i * 2, (n - i - 1) * 2) << endl;
}
return 0;
}
运行结果:
E. X进制减法
这道题的难点就是X进制怎么转换成10进制
比如题目的321,从左到右分别是8/10/2进制,那么转成十进制应该是1 + 2 * (2) + 3 * (10 * 2)
,每一个数位都是乘上右边所有数的进制数相乘。
我的做法是贪心,不知道对不对,题目说了A>B,那肯定是进制越小最后的差越小
所以我的每一位的进制数 都是A和B这个位的数值 大的那个 加一
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
int main() {
int n;
cin >> n;
int ma, mb;
cin >> ma;
vector<int> aa(ma, 0);
for(int i = 0; i < ma; ++i) {
cin >> aa[i];
}
cin >> mb;
vector<int> bb(mb, 0);
for(int i = 0; i < mb; ++i) {
cin >> bb[i];
}
// ======================================
int mab = max(ma, mb);
vector<int> a(mab, 0);
vector<int> b(mab, 0);
for(int i = 0; i < ma; ++i) {
a[ma - i - 1] = aa[i];
}
for(int i = 0; i < mb; ++i) {
b[mb - i - 1] = bb[i];
}
ll res = 0;
ll p = 1;
for(int i = 0; i < mab; ++i) {
res += (a[i] - b[i]) * p;
res %= mod;
p *= max(max(a[i], b[i]) + 1, 2);
p %= mod;
}
cout << res << endl;
return 0;
}
运行结果:
F. 统计子矩阵
我是直接前缀和暴力了,不出意外肯定超时,太菜了想来想去就是想不出该怎么优化。。。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
ll m,n,k;
cin >> m >> n >> k;
vector<vector<int> > v(m, vector<int>(n));
for(int i = 0; i < m; ++i) {
for(int j = 0; j < n; ++j) {
cin >> v[i][j];
}
}
vector<vector<int> > p(m + 1, vector<int>(n + 1));
for(int i = 1; i <= m; ++i) {
for(int j = 1; j <= n; ++j) {
p[i][j] = p[i - 1][j] + p[i][j-1] - p[i-1][j-1] + v[i-1][j-1];
}
}
ll res = 0;
for(int i = 1; i <= m; ++i) {
for(int j = 1; j <= n; ++j) {
for(int a = 0; a < i; ++a) {
for(int b = 0; b < j; ++b) {
if(p[i][j] - p[i][b] - p[a][j] + p[a][b] <= k) ++res;
}
}
}
}
cout << res << endl;
return 0;
}
运行结果:果然是超时了
G. 积木画
这个是经典DP了,有手就行,力扣有原题:https://leetcode-cn.com/problems/domino-and-tromino-tiling/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
int main() {
ll n;
cin >> n;
vector<ll> dp(n+1);
dp[0] = 1;
ll p = 0;
for(int i = 1; i <= n; ++i) {
// I 竖着摆
dp[i] += dp[i-1];
dp[i] %= mod;
if(i >= 2) {
// 两个I横着摆
dp[i] += dp[i-2];
dp[i] %= mod;
}
// 两个L型或者两个L型夹着若干个横着的I型
dp[i] += p * 2;
dp[i] %= mod;
if(i >= 2) {
p += dp[i-2];
p %= mod;
}
}
cout << dp[n] << endl;
return 0;
}
运行结果:
H. 扫雷
这题我的做法就是先建图,然后DFS,有一点注意的是他的数据量很大,建图的时候如果两层循环遍历,不出意外的话肯定会超时,但是可以发现r是很小的,所以我的做法是用两层的map
,记录这个位置有哪些炸弹,然后枚举他附近的位置。
这题让我有点疑惑的是,如果有第二个排雷火箭,那第一次被炸掉了第二次还算不算,应该不算了吧。。
力扣上面有道很像的:https://leetcode-cn.com/problems/detonate-the-maximum-bombs/
这道好像没做出来,代码就不放了
I. 李白打酒
这题应该就是个DP,我做的时候没那么多时间了,直接丢了一个暴力就提交上去了,后面回去我补了一个DP的版本
暴力版本:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
map<ll, int> dp[110][110];
const ll mod = 1000000007;
int f(int i, int j, ll k) {
if(i == 0) {
return j == k;
}
if(dp[i][j].find(k) != dp[i][j].end()) {
return dp[i][j][k];
}
if(i > 0){
dp[i][j][k] += f(i-1 ,j ,k * 2);
dp[i][j][k] %= mod;
}
if(j > 1) {
dp[i][j][k] += f(i, j-1, k - 1);
dp[i][j][k] %= mod;
}
return dp[i][j][k];
}
int main() {
cin >> n >> m;
cout << f(n,m,2) << endl;
return 0;
}
运行结果:超时了
DP版本:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
int main() {
int n,m;
cin >> n >> m;
vector<vector<vector<int > > > dp(n + 1, vector<vector<int> >(m + 1,vector<int>(m + 1)));
dp[0][1][1] = 1;
for(int i = 0; i <= n; ++i) {
for(int j = 0; j <= m; ++j) {
for(int k = 0; k <= m; ++k) {
if(k > 0 && j >= 1) {
dp[i][j][k] += dp[i][j-1][k-1];
}
if(k * 2 <= m && i >= 1) {
dp[i][j][k] += dp[i-1][j][k*2];
}
dp[i][j][k] %= mod;
}
}
}
cout << dp[n][m][2] << endl;
return 0;
}
运行结果:已通过
J. 砍竹子
贪心,用一个优先队列,每次都是挑最大的下手,处理这个数的时候再向左右两边扩散,高度一样的一起干了。
发现一个规律,那些数只有最开始的时候是随机的,只要经过一次sqrt(x / 2 + 1)
的处理,这个数以后都是1/6/16/30/…,一个数和他旁边的数一旦相等,以后永远都相等,所以,我对一个数进行sqrt(x / 2 + 1)
处理之后,会将它重新放回优先队列,如果他隔壁的数和他原来相等,我会顺便对他隔壁的数进行sqrt(x / 2 + 1)
处理,但是不会再放回优先队列。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct P{
ll x;
int idx;
P(ll a, int b) : x(a), idx(b){}
bool operator<(const P& p) const {
return this->x < p.x;
}
};
int main() {
int n;
cin >> n;
vector<ll> v(n);
for(int i = 0; i < n; ++i) cin >> v[i];
priority_queue<P, vector<P> > pq;
for(int i = 0; i < n; ++i) {
if(v[i] > 1) {
pq.push(P(v[i], i));
}
}
ll res = 0;
while(!pq.empty()) {
P p = pq.top();
pq.pop();
if(v[p.idx] != p.x) {
continue;
}
++res;
ll x = p.x;
int idx = p.idx;
v[idx] = sqrt(x / 2 + 1);
if(v[idx] > 1) {
pq.push(P(v[idx], idx));
}
int i = idx - 1;
while(i >= 0 && v[i] == x) {
v[i--] = sqrt(x / 2 + 1);
}
i = idx + 1;
while(i < n && v[i] == x) {
v[i++] = sqrt(x / 2 + 1);
}
}
cout << res << endl;
return 0;
}
运行结果:
总结:希望混个省一,赚回报名费