【DP】[USACO 2016 February Contest, Gold]Circular Barn Revisited
题目大意
还是这个谷仓,有n(3<=n<=100)个房间。当然,奶牛可能不止n头了。奶牛都在谷仓外面。现在约翰想要让第i个房间关ri(1<=ri<=1000000)头奶牛按顺时针方向走,直到到达合适的房间。这k(1<=k<=7)个门开在哪里,才能使得奶牛们走的路程最少。奶牛在谷仓外可以随意移动,可以随意选择k个门中的任意一个排队,这不计入最终的路程。
输入格式:
输入包含n和k。接下来n行包含r1,r2,……rn,表示每个房间最后容纳的奶牛数目。
输出格式:奶牛们走的最小的总路程。
输入样例:
6 2
2
5
4
2
6
2
输出样例:
14
样例解释:FJ可以打开2号门和5号门。这样,11头奶牛从2号门进入,走过的总距离为8,到达2,3,4号房间。10头奶牛从5号门进入,分别叨叨5号,6号和1号房间,走过的总距离为6.
分析
首先,预处理出
然后破环为链,进行dp。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 100
#define MAXK 7
int n,r[MAXN+10],k;
long long f[MAXN*2+1][MAXN+1][MAXK+1],ans=0x7fffffffffffffffll,dist[MAXN+10][MAXN+10];
void Read(int &x){
char c;
while(c=getchar(),c!=EOF)
if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
return;
}
}
void read(){
Read(n),Read(k);
int i;
for(i=1;i<=n;i++)
Read(r[i]);
}
void prepare(){
int i,j;
for(i=1;i<=n;i++){
dist[i][1]=0;
for(j=2;j<=n;j++)
dist[i][j]=r[(i+j-2)%n+1]*(j-1)+dist[i][j-1];
}
}
void dp(){
int i,j,l,t=2*n,p;
memset(f,0x3f,sizeof f);
for(i=0;i<=t;i++)
f[i][0][0]=0;
for(i=1;i<=n;i++){
for(j=1;j<=n&&j<=i;j++)
for(l=1;l<=k;l++)
for(p=1;p<=j;p++)
f[i][j][l]=min(f[i][j][l],f[i-p][j-p][l-1]+dist[i-p+1][p]);
ans=min(ans,f[i][n][k]);
}
for(i=n+1;i<=t;i++){
for(j=1;j<=n;j++)
for(l=1;l<=k;l++)
for(p=1;p<=j;p++)
f[i][j][l]=min(f[i][j][l],f[i-p][j-p][l-1]+dist[(i-p)%n+1][p]);
ans=min(ans,f[i][n][k]);
}
}
int main()
{
read();
prepare();
dp();
printf("%lld\n",ans);
}