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

来源:互联网 发布:读书笔记软件下载 编辑:程序博客网 时间:2024/06/13 04:19

题意

这里写图片描述
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],所以ja[i]<=kb[i]kj>=b[i]a[i]
满足kj=b[i]a[i]的(j,k)对会只剩一个x’能够包含,所以这些x’必须要转移。不难得出这些x’就是[xbi,xai]

那么不卡在这个限制上的(j2,k2)对能被x[xbi,xai]包含吗?是会被包含的。 比较显然,而且证明起来很难表述。
手玩几个试试就知道了,因为它x’的区间与[xbi,xai]是一定有交集的。

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

Demo

#include <cstdio>#include <iostream>#include <cstring>const int N=1010,P=1e4+10;using namespace std;int n,p;int a[N],b[N],c[N],ans;int f[N][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[n][p]==f[n+1][0]) printf("IMPOSSIBLE!!!\n"); else         printf("%d\n",f[n][p]);    }}
原创粉丝点击