您的位置:首页 > 其它

POJ 1160 区间DP + 平行四边形优化

2017-08-28 09:14 441 查看

区间DP + 平行四边形优化

题意:

​ 有n个村庄现在要建立m个邮局,问怎么建邮局才能使得村庄到最近的邮局距离和最小。输出距离和即可。

思路:

​ 一般的区间dp是从小区间到大区间,而此题在此之外还有一个限制是m个邮局。对于此类问题可以直接建立dp的时候加上限制条件:dp[i][j]=min(dp[k][j−1]+one[k+1][i]) 定义dp含义为前i个村庄建立了j个邮局的最小距离和,那么在建立第j的时候可以枚举之前已经求出的区间,从j-1个邮局的前提下加上现在的1个邮局,one[i][j] 的含义是在区间i到j 的范围之中建立一个邮局,其实也就是中位数的位置,然后递推找出最小的值即可。

未优化代码。

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 305;
const int INF = 0x3f3f3f3f;

int n,m;
int a[maxn];
int one[maxn][maxn];
int dp[maxn][maxn];

int main()
{
//freopen("in.txt","r",stdin);

scanf("%d%d",&n,&m);
for(int i = 1;i <= n; i++) {
scanf("%d",&a[i]);
}
for(int i = 1;i <= n; i++) {
for(int j = i;j <= n; j++) {
if(i == j) one[i][j] = 0;
else one[i][j] = one[i][j-1] + a[j] - a[(i+j)/2];
}
}
memset(dp,INF,sizeof(dp));
for(int i = 1;i <= n; i++) dp[i][1] = one[1][i];
for(int i = 2;i <= n; i++) {
int End = min(m,i);
for(int j = 2;j <= End; j++) {
for(int k = 1;k <= i; k++) {
if(dp[i][j] > dp[k][j-1] + one[k+1][i])
dp[i][j] = dp[k][j-1] + one[k+1][i];
}
}
}
printf("%d\n",dp
[m]);
return 0;
}


可以用四边形优化

平行四边形的ss数组是优化的核心,但是它是建立在:m[i,j]=minm[i,k]+m[k,j](i≤k≤j) 条件下的。

而ss[i,j−1]≤ss[i,j]≤ss[i+1,j] 是最优解的优化,但是不知道ss[i+1][j] 如何算出,但是可以知道原本的上界i+1。

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 305;
const int INF = 0x3f3f3f3f;

int n,m;
int a[maxn];
int one[maxn][maxn];
int dp[maxn][maxn];
int ss[maxn][maxn];

int main()
{
//freopen("in.txt","r",stdin);

scanf("%d%d",&n,&m);
for(int i = 1;i <= n; i++) {
scanf("%d",&a[i]);
}
for(int i = 1;i <= n; i++) {
for(int j = i;j <= n; j++) {
if(i == j) one[i][j] = 0;
else one[i][j] = one[i][j-1] + a[j] - a[(i+j)/2];
}
}
memset(dp,INF,sizeof(dp));
memset(ss,0,sizeof(ss));
for(int i = 1;i <= n; i++) dp[i][1] = one[1][i];
for(int i = 2;i <= n; i++) {
int End = min(m,i);
for(int j = 2;j <= End; j++) {
ss[i+1][j] = i+1;
for(int k = ss[i][j-1];k <= ss[i+1][j]; k++) {
if(dp[i][j] > dp[k][j-1] + one[k+1][i]) {
dp[i][j] = dp[k][j-1] + one[k+1][i];
ss[i][j] = k;
}
}
}
}
printf("%d\n",dp
[m]);
return 0;
}


还有一个版本是i和j的循环可以换一换

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define inf 0x3f3f3f3f
int dp[305][305];
int x[305],one[305][305]={0};
int K[305][305];
int main()
{
int N,M,i,j,k;
// freopen("in.txt","r",stdin);
while(cin>>N>>M){
for(i=1;i<=N;++i)  scanf("%d",&x[i]);
memset(dp,inf,sizeof(dp));
for(i=1;i<=N;++i)
for(j=i;j<=N;++j)
if(i==j) one[i][i]=0;
else
one[i][j]=one[i][j-1]+x[j]-x[(i+j)/2];
for(i=1;i<=N;++i) dp[i][1]=one[1][i];
for(i=1;i<=N;++i) K[i][i]=1;
for(j=2;j<=M;++j)
{
for(i=1;i<=N;++i)
{
K[i+1][j]=i+1;
if(j>i) continue;
for(k=K[i][j-1];k<=K[i+1][j];++k)
{
int s=dp[k][j-1]+one[k+1][i];
if(dp[i][j]>s) {
dp[i][j]=s;
K[i][j]=k;
}
}
}
}
printf("%d\n",dp
[M]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dp poj