洛谷
由于环非常难处理,但是这个数据范围又很小,所以很容易想到枚举一个谷仓的位置,然后以这个地点为起始点使用动态规划。
为了使转移方便,我这里选择了逆时针处理这样计算这一段奶牛的路程。
我们定义 \(dp_{i,j}\) 表示已经选择了 \(i\) 个谷仓,目前最后一个选择的谷仓为 \(j\) 时的最少路程。
我们再处理出前缀和以及从第一个点开始奶牛走的总路程,方便我们转移。
最后就可以得到转移方程:
\[$
dp_{i,j}=\min(dp_{i-1,k}+j\times (pre_j-pre_k)-s_j+s_k)
$\]
这样就可以完成暴力部分代码了。
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, m, r[1005], pre[1005], s[1005], ans = 1e16, dp[1005][1005], p[1005], head, tail, x[1005], y[1005];
void solve() {for (int i = 1; i <= n; i++) pre[i] = pre[i - 1] + r[i];for (int i = 1; i <= n; i++) s[i] = s[i - 1] + r[i] * i;memset(dp, 0x3f, sizeof(dp));dp[0][0] = 0;for (int i = 1; i <= m; i++) {for (int j = 1; j <= n; j++) {for (int k = 0; k < j; k++) {dp[i][j] = min(dp[i][j], dp[i - 1][k] + j * (pre[j] - pre[k]) - s[j] + s[k]);}}}ans = min(ans, dp[m][n]);
}
signed main() {cin >> n >> m;for (int i = n; i >= 1; i--) cin >> r[i];for (int i = 1; i <= n; i++) {for (int j = 0; j < n; j++) r[j] = r[j + 1];r[n] = r[0];solve();}cout << ans;return 0;
}
考虑如何优化。
首先在原式子中之中 \(i\) 这一状态对决策不产生影响,不进行考虑。
此时我们发现可以使用斜率优化。
我们将确定两个变量 \(dp_{k}+s_k\) 以及 \(pre_k\)。
直接维护一个下凸包即可。
代码:
/*
dp[j]=min(dp[k]+j*(pre[j]-pre[k])-s[j]+s[k])
x<y,y优
dp[x]-i*pre[x]+s[x]>dp[y]-i*pre[y]+s[y]
dp[x]-dp[y]+s[x]-s[y]>(pre[x]-pre[y])*i
*/
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, m, r[1005], pre[1005], s[1005], ans = 1e16, dp[1005], p[1005], head, tail, x[1005], y[1005];
bool check(int i, int j, int k) { return (y[i] - y[j]) * (x[j] - x[k]) <= (y[j] - y[k]) * (x[i] - x[j]); }
void solve() {for (int i = 1; i <= n; i++) pre[i] = pre[i - 1] + r[i];for (int i = 1; i <= n; i++) s[i] = s[i - 1] + r[i] * i;memset(dp, 0x3f, sizeof(dp));dp[0] = 0;for (int i = 1; i <= m; i++) {head = 1, tail = 0;p[++tail] = 0;for (int j = 1; j <= n; j++) y[j] = dp[j] + s[j], x[j] = pre[j];for (int j = 1; j <= n; j++) {while (head < tail && y[p[head]] - y[p[head + 1]] > j * (x[p[head]] - x[p[head + 1]])) head++;dp[j] = y[p[head]] - x[p[head]] * j + j * pre[j] - s[j];while (head < tail && check(j, p[tail], p[tail - 1])) tail--;p[++tail] = j;}}ans = min(ans, dp[n]);
}
signed main() {cin >> n >> m;for (int i = n; i >= 1; i--) cin >> r[i];for (int i = 1; i <= n; i++) {for (int j = 0; j < n; j++) r[j] = r[j + 1];r[n] = r[0];solve();}cout << ans;return 0;
}