前言
少输出个字母s,好不容易看出来了,然后提交的时候又没注释文件读写,对拍的时候又造错数据,AC代码数组越界了,debug半天才发现原来写的就是对的。
题目
有n(<=200)个餐厅位于x轴上。有m(<=30)个供应商,每个供应商可以选择x轴上的一个位置,为任意k个餐厅提供供应。一个供应商的花费为他到所有他供应的餐厅的距离之和。问每个餐厅都要有一家供应商提供供应,所有供应商的最小花费和是多少。所有餐厅和供应商都在整数点上。
解题思路
d p [ i ] [ j ] dp[i][j] dp[i][j]表示前 i i i个饭店, j j j个供应商的最小花费。
d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ k ] [ j − 1 ] + w [ k ] [ i ] ) dp[i][j]=min(dp[i][j],dp[k][j-1]+w[k][i]) dp[i][j]=min(dp[i][j],dp[k][j−1]+w[k][i])
w [ k ] [ i ] w[k][i] w[k][i]表示一个供应商为 k k k到 i i i号餐厅提供服务的最小花费,结论:一段直线上的最优点就是中位数。可以 n 3 n^3 n3暴力预处理。
枚举k转移状态。前 j − 1 j-1 j−1个供应商为前 k k k家餐厅提供供应,第 j j j个供应商为 [ k + 1 , i ] [k+1,i] [k+1,i]家餐厅提供服务。
代码
#include <bits/stdc++.h>
using namespace std;
int a[210];
int n, m;
int med[210][210];
int w[210][210];
int dp[210][35]; //dp[i][j]表示前i个饭店,有j个供应商的最小贡献
struct node2
{
int l, r;
} ans[210][210];
int CA1, CA2;
void dfs(int a, int b) //前a个饭店,b个供应商时 最后一个供应商服务区间就是 [ans[a][b].l,ans[a][b].r]
{
if (!b)
return;
dfs(ans[a][b].l - 1, b - 1);
if (ans[a][b].l != ans[a][b].r)
printf("Depot %d at restaurant %d serves restaurants %d to %d\n", ++CA2, ans[a][b].l + ans[a][b].r >> 1, ans[a][b].l, ans[a][b].r);
else
printf("Depot %d at restaurant %d serves restaurant %d\n", ++CA2, ans[a][b].r, ans[a][b].r);
}
void solve()
{
printf("Chain %d\n", ++CA1);
CA2 = 0;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n; i++)
{
for (int j = i; j <= n; j++)
{
w[i][j] = 0;
for (int k = i; k <= j; k++)
w[i][j] += abs(a[i + j >> 1] - a[k]);
}
}
memset(dp, 0x3f3f3f3f, sizeof dp);
for (int j = 1; j <= n; j++)
{
dp[j][1] = w[1][j];
ans[j][1] = node2{1, j};
}
for (int i = 1; i <= n; i++)
for (int j = 2; j <= m; j++) //
{
for (int mid = 1; mid < i; mid++)
{
if (dp[i][j] > dp[mid][j - 1] + w[mid + 1][i])
{
dp[i][j] = dp[mid][j - 1] + w[mid + 1][i];
ans[i][j] = node2{mid + 1, i}; //保存最后一个供应商的服务区间信息
}
}
}
dfs(n, m);
printf("Total distance sum = %d\n\n", dp[n][m]);
}
int main()
{
while (cin >> n >> m && (n + m))
solve();
return 0;
}