青岛市赛2021 初中组 T2 跳格子 题解
AdrianLazer · · 题解
跳格子
5分 a_{i, j} \leq 0
显然可以全取一遍
#include <stdio.h>
long long ans;
int n, m, a;
int main() {
scanf("%d%d", &n, &m); n *= m;
for (; n; --n) scanf("%d", &a), ans += a;
printf("%lld", ans);
return 0;
}
另外10分 n, m \leq 5
Depth First Search
#include <stdio.h>
int n, m, a[1005][1005];
long long ans = 0x3fffffffffffffff;
void dfs(int k, int rmost, long long sum) {
if (!k) {
if (sum < ans) ans = sum;
return;
}
long long nsum = 0;
for (int i = rmost; i; --i) {
nsum += a[k][i];
for (int j = i; j <= rmost; ++j)
dfs(k - 1, j, sum + nsum);
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
scanf("%d", a[i] + j);
for (int i = 1; i <= m; ++i)
dfs(n, i, 0);
printf("%lld", ans);
return 0;
}
好像得了20分
另外15分 n = 2
枚举向上走的点,记录前缀和、后缀和、前缀和的前缀最大、后缀和的后缀最大,这样能
#include <stdio.h>
int n, m, a[1005][1005], sump[1005][1005], maxp[1005][1005], sums[1005][1005], maxs[1005][1005], ans;
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
scanf("%d", a[i] + j);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
sump[i][j] = sump[i][j - 1] + a[i][j];
maxp[i][j] = maxp[i][j - 1];
if (sump[i][j] > maxp[i][j]) maxp[i][j] = sump[i][j];
}
for (int i = 1; i <= n; ++i)
for (int j = m; j; --j) {
sums[i][j] = sums[i][j + 1] + a[i][j];
maxs[i][j] = maxs[i][j + 1];
if (sums[i][j] > maxs[i][j]) maxs[i][j] = sums[i][j];
}
for (int i = 1; i <= m; ++i)
if (sums[2][i] - maxs[2][i + 1] - maxp[1][i - 1] + sump[1][i] < ans)
ans = sums[2][i] - maxs[2][i + 1] - maxp[1][i - 1] + sump[1][i];
printf("%d", ans);
return 0;
}
25分是什么鬼
另外20分 n, m \leq 90
不会,留给乱搞
另外20分 n, m \leq 400
设
时间复杂度
#include <stdio.h>
int n, m, a[1005][1005], sum[1005][1005], maxx[1005][1005];
long long dp[1005][1005], ans = 0x3fffffffffffffff;
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
scanf("%d", a[i] + j);
sum[i][j] = sum[i][j - 1] + a[i][j];
maxx[i][j] = maxx[i][j - 1];
if (sum[i][j] > maxx[i][j]) maxx[i][j] = sum[i][j];
}
for (int i = n; i; --i)
for (int j = 1; j <= m; ++j) {
dp[i][j] = 0x3fffffffffffffff;
for (int k = j; k <= m; ++k)
if (dp[i + 1][k] + sum[i][k] - maxx[i][j - 1] < dp[i][j])
dp[i][j] = dp[i + 1][k] + sum[i][k] - maxx[i][j - 1];
}
for (int j = m; j; --j)
if (dp[1][j] < ans) ans = dp[1][j];
printf("%lld", ans);
return 0;
}
好像开O2可以艹过去
常数小的O(nm^2)一秒完全不虚
100分 n, m \leq 10^3
考虑优化转移。这个然后这就是个单点修改区间查最小值的问题,线段树解决
一定又是数据结构学傻了。
可以直接维护后缀最小来解决,当然也可以直接用
#include <stdio.h>
#include <string.h>
int n, m, a[1005][1005], maxx[1005][1005], sum[1005][1005];
long long dp[1005][1005], ans = 0x3fffffffffffffff;
inline long long min(long long a, long long b) { return a < b ? a : b; }
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
scanf("%d", a[i] + j);
sum[i][j] = a[i][j] + sum[i][j - 1];
maxx[i][j] = maxx[i][j - 1] > sum[i][j] ? maxx[i][j - 1] : sum[i][j];
}
memset(dp, 0x3f, sizeof dp);
for (int j = m; j; --j) dp[n][j] = min(sum[n][j], dp[n][j + 1] + maxx[n][j]) - maxx[n][j - 1];
for (int i = n - 1; i; --i)
for (int j = m; j; --j)
dp[i][j] = min(dp[i + 1][j] + sum[i][j], dp[i][j + 1] + maxx[i][j]) - maxx[i][j - 1];
for (int j = m; j; --j) ans = min(ans, dp[1][j]);
printf("%lld", ans);
return 0;
}