您的位置:首页 > 其它

jzoj5259 线性规划问题 (巧妙设状态的dp)

2017-08-11 16:20 267 查看

题意



n<=1e3,p<=1e4

保证ai<=bi

分析

暴力做法很简单,设fi,j,k表示suma=j,sumb=k的最小的c就可以了,转移显然.

观察题目给的约束,j,k分别在P的两边。

我们简化状态,设fi,x表示i,j分别在j两边的最小c值。

如何转移?

考虑x的意义j<=x<=k

因为是取min,所以重复是没有问题的。 只需要考虑不漏下。

感性理解:

想象一个数轴上有j<=x<=k,j往前a[i],k往前b[i],一定在这些j′,k′中间的x′能怎么取?因为a[i]<=b[i],所以j−a[i]<=k−b[i],也就是k−j>=b[i]−a[i]。

满足k−j=b[i]−a[i]的(j,k)对会只剩一个x’能够包含,所以这些x’必须要转移。不难得出这些x’就是[x−bi,x−ai]

那么不卡在这个限制上的(j2,k2)对能被x′∈[x−bi,x−ai]包含吗?是会被包含的。 比较显然,而且证明起来很难表述。

手玩几个试试就知道了,因为它x’的区间与[x−bi,x−ai]是一定有交集的。

这样直接用单调队列维护就可以了。

O(np)

Demo

#include <cstdio>
#include <iostream>
#include <cstring>
const int N=1010,P=1e4+10;
using namespace std;
int n,p;
int a
,b
,c
,ans;
int f
[P];
int Q[P],head,tail;
int main() {
freopen("3.in","r",stdin);
int T;
for (cin>>T; T; T--) {
cin>>n>>p;
for (int i=1; i<=n; i++) scanf("%d",&a[i]);
for (int i=1; i<=n; i++) scanf("%d",&b[i]);
for (int i=1; i<=n; i++) scanf("%d",&c[i]);
memset(f,127,sizeof f); f[0][0]=0;
for (int i=1; i<=n; i++) {
head=1; int rd=0; tail=0;
for (int j=0; j<=p; j++) {
for (; rd<=j-a[i]; ++rd) {
while (head<=tail && f[i-1][ Q[tail] ]>=f[i-1][rd]) --tail;
Q[++tail]=rd;
}
while (head<=tail && Q[head]<j-b[i]) ++head;
f[i][j]=f[i-1][j];
if (head<=tail) f[i][j]=min(f[i][j],f[i-1][Q[head]]+c[i]);
}
}

if (f
[p]==f[n+1][0]) printf("IMPOSSIBLE!!!\n"); else
printf("%d\n",f
[p]);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: