题意:一栋n层的楼,每层有ai个打乒乓球的人和bi个游泳的人,每层可以建一个游泳池或乒乓球室,每人人到其喜欢项目场所的最短距离的总和为m,求最小的m(且至少要有一个游泳池和一个乒乓球室)
题解:依然是用动态规划去求解 dp[i][j] 中j为0表示在这i层建游泳池,且j+1层不能建游泳池,这样dp[i][0]可以由dp[j][1]推过来(j<i)表示j+1和i之间都建游泳池,因为总的来说分为若干段的0和1 那么 dp[i][0]的意义就是 i这一位是一段0的结尾,而计算花费时 对于一段连续的0,前半部分转向前面的1,后半部分转向后面的1.具体花费通过另外两个数组来计算
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
#include<set>
#define scnaf scanf
#define cahr char
#define bug puts("=========================");
using namespace std;
typedef long long ll;
const int mod=1000000007;
const int maxn=4000+5;
const ll inf=1e17;
ll a[maxn][2];
ll dp[maxn][2];
ll sum[maxn][2];
ll f[maxn][2];
ll rf[maxn][2];
int n;
void init(){
sum[0][0]=sum[0][1]=0;
f[0][0]=f[0][1]=0;
for(int now=0;now<=1;now++){
for(int i=1;i<=n;i++){
sum[i][now]=sum[i-1][now]+a[i][now];
f[i][now]=f[i-1][now]+a[i][now]*i;
}
rf[n+1][now]=0;
for(int i=n;i>=1;i--)
rf[i][now]=rf[i+1][now]+a[i][now]*(n-i+1);
}
}
ll SUM(int i,int j,int now){
return sum[j][now]-sum[i-1][now];
}
ll cost(int now,int i,int j){
if(i==j)return 0;
if(j>i) return f[j][now]-f[i-1][now]-SUM(i,j,now)*i;
return rf[j][now]-rf[i+1][now]-SUM(j,i,now)*(n-i+1);
}
int main()
{
int T_T,test=1;
scanf("%d",&T_T);
while(T_T--){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld%lld",&a[i][0],&a[i][1]);
init();
for(int i=1;i<n;i++)
for(int now=0;now<=1;now++){
dp[i][now]=cost(now,i+1,1);
}
for(int i=1;i<=n;i++){
for(int now=0;now<=1;now++)//第i位为now
{
if(i==n)
{
dp[n][now]=inf;
for(int j=1;j<=n-1;j++){
dp[n][now]=min(dp[n][now],dp[j][!now]+cost(now,j,n));
}
continue;
}
for(int j=1;j<=i-1;j++){
int mid=(i+j+1)/2;
ll v=dp[j][!now]+cost(now,j,mid)+cost(now,i+1,mid+1);
dp[i][now]=min(dp[i][now],v);
}
}
}
printf("Case #%d: %lld\n",test++,min(dp[n][0],dp[n][1]));
}
}