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

题意

jzoj5259 线性规划问题 (巧妙设状态的dp)_第1张图片
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 
#include 
#include 
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]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]);
    }
}

你可能感兴趣的:(题解,DDDDDppppp)