题干:
总时间限制:
1000ms
内存限制:
65536kB
描述
政府在某山区修建了一条道路,恰好穿越总共m个村庄的每个村庄一次,没有回路或交叉,任意两个村庄只能通过这条路来往。已知任意两个相邻的村庄之间的距离为di(为正整数),其中,0 < i < m。为了提高山区的文化素质,政府又决定从m个村中选择n个村建小学(设 0 < n < = m < 500 )。请根据给定的m、n以及所有相邻村庄的距离,选择在哪些村庄建小学,才使得所有村到最近小学的距离总和最小,计算最小值。
输入
第1行为m和n,其间用空格间隔
第2行为(m-1) 个整数,依次表示从一端到另一端的相邻村庄的距离,整数之间以空格间隔。
例如
10 3
2 4 6 5 2 4 3 1 3
表示在10个村庄建3所学校。第1个村庄与第2个村庄距离为2,第2个村庄与第3个村庄距离为4,第3个村庄与第4个村庄距离为6,...,第9个村庄到第10个村庄的距离为3。
输出
各村庄到最近学校的距离之和的最小值。
样例输入
10 2
3 1 3 1 1 1 1 1 3
样例输出
18
解题报告:
dp[i][j]代表前i个村庄建立了j所小学的最小距离。
AC代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e5 + 5;
const int INF = 0x3f3f3f3f;
//从m个村中选择n个村建小学(设 0 < n < = m < 500 )
int dis[555][555];
int f[555][555];
int dp[555][555];//dp[i][j] 代表前i个村庄创建n个j个学校的最小距离。
int go(int i,int j) {
int mid = (i+j)>>1;
int res = 0;
for(; i<=j; i++) res += dis[i][mid];
return res;
}
int n,m;
int main() {
cin>>m>>n;
for(int i = 1; i<=m; i++) dis[i][i] = 0;
for(int x,i = 2; i<=m; i++) {
scanf("%d",&x);
dis[i-1][i] = dis[i][i-1] = x;//其实这一句可以合并到下面,因为你dis[i][i]=0了。。
for(int j = 1; j<i; j++) {
dis[j][i] = dis[j][i-1] + x;
dis[i][j] = dis[j][i];
}
}
for(int i = 1; i<=m; i++) {
for(int j = 1; j<=m; j++) {
f[i][j] = go(i,j);
}
}
memset(dp,INF,sizeof dp);
for(int i = 1; i<=m; i++) dp[i][1] = f[1][i];
for(int i = 2; i<=m; i++) {
for(int j = 2; j<=n; j++) {
if(j > i) continue;
for(int k = 1; k<i; k++) {
dp[i][j] = min(dp[i][j],dp[k][j-1] + f[k+1][i]);
}
}
}
printf("%d\n",dp[m][n]);
return 0 ;
}
//19:11 - 19:28
简化一点的:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e5 + 5;
const int INF = 0x3f3f3f3f;
//从m个村中选择n个村建小学(设 0 < n < = m < 500 )
int sum[555];
int f[555][555];
int dp[555][555];//dp[i][j] 代表前i个村庄创建n个j个学校的最小距离。
int go(int i,int j) {
int mid = (i+j)>>1;
int res = 0;
for(; i<=j; i++) res += abs(sum[mid]-sum[i]);
return res;
}
int n,m;
int main() {
cin>>m>>n;
for(int x,i = 2; i<=m; i++) {
scanf("%d",&x);
sum[i] = sum[i-1] + x;//边的前缀和
}
for(int i = 1; i<=m; i++) {
for(int j = 1; j<=m; j++) {
f[i][j] = go(i,j);
}
}
memset(dp,INF,sizeof dp);
for(int i = 1; i<=m; i++) dp[i][1] = f[1][i];
for(int i = 2; i<=m; i++) {
for(int j = 2; j<=n; j++) {
if(j > i) continue;
for(int k = j-1; k<i; k++) {
dp[i][j] = min(dp[i][j],dp[k][j-1] + f[k+1][i]);
}
}
}
printf("%d\n",dp[m][n]);
return 0 ;
}
//19:11 - 19:28
或者分治:(但是这个耗时400ms??上面那个dp只有100多ms)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
#define rep(i,a,b) for(int i=a;i<b;i++)
using namespace std;
const int MAX = 2e5 + 5;
const ll INF=1e15;
int a[MAX];
ll dp[MAX],ndp[MAX],s[MAX];
ll cal(int l,int r) {
return s[r]+s[l-1]-s[(l+r-1)/2]-s[(l+r)/2];
}
void solve(int l,int r,int p,int q) {
if(l>r)return;
int m=(l+r)>>1,x=p;
ndp[m]=INF;
for(int i = p; i<=q; i++) {
ll w=dp[i]+cal(i+1,m);
if(ndp[m]>w) {
ndp[m]=w;
x=i;
}
}
if(l<r) {
solve(l,m-1,p,x);
solve(m+1,r,x,q);
}
}
int main() {
int T,ca=0,k,i,j,m=0,K,n;
scanf("%d%d",&n,&K);
K = min(K,n);
a[1]=0;
for(int i = 2; i<=n; i++) {
int q;
scanf("%d",&q);
a[i] = a[i-1] + q;
}
sort(a+1,a+n+1);
for(int i = 1; i<=n; i++)s[i]=s[i-1]+a[i];
for(int i = 1; i<=n; i++) dp[i]=INF;
for(int i = 1; i<=K; i++) {
solve(1,n,0,n);
swap(dp,ndp);
}
printf("%lld\n",dp[n]);
return 0 ;
}