codeforces 500F New Year Shopping(dp)

l i n k link link:添加链接描述


显然考的是0/1背包
重复运算很浪费时间 就可以考虑分组去优化运算

为了方便说明/分组 我们把持续时间移到我们访问的时间 也就是一个物品变成一个时间点 把我们去的时间变成一段

那么本来 t i t_i ti 时刻去 会转化成 [ t i − p + 1 , t i ] [t_i-p+1,t_i] [tip+1,ti]

我们考虑如何分组减少背包的次数

很显然我们先对物品和时间都进行排序

那么我们取 T = 1 , p + 1 , 2 p + 1.... T=1,p+1,2p+1.... T=1,p+1,2p+1.... 那么所有的区间都会被覆盖一次,我们只要以 T T T 为中轴 所有 d p dp dp 计算时合并即可

边界细节挺多的。。

#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define fr first
#define se second
int rd() {
    int sum = 0;char c = getchar();bool flag = true;
    while(c < '0' || c > '9') {if(c == '-') flag = false;c = getchar();}
    while(c >= '0' && c <= '9') sum = sum * 10 + c - 48,c = getchar();	
    if(flag) return sum;
    else return -sum;
}
struct item {
	int c,v,t;
}a[4010];
struct host {
    int t,c,id;
}b[20100];
int n,p,q;
bool cmp1(item a,item b) {return a.t < b.t;}
bool cmp2(host a,host b) {return a.t < b.t;}
void init() {
    n = rd();p = rd();
    rep(i,1,n) a[i].c = rd() , a[i].v = rd() , a[i].t = rd();
    q = rd();
    rep(i,1,q) b[i].t = rd() , b[i].c = rd() , b[i].id = i;
    sort(a+1,a+n+1,cmp1);
    sort(b+1,b+q+1,cmp2);
    return;
}
//???ost??说  ????[t-p+1,t];
int dp[2][4050][4050];
int ans[20100];
void merge(int A,int B,int C,int D,int xxx,int yyy) {
    int l = A,r = C-1;
    rep(i,xxx,yyy) { 
        int L = b[i].t-p+1,R = b[i].t;
        while(a[l].t < L && l <= B) l++;
        while(a[r+1].t <= R && r < D) r++;
        int id_l = B-l+1 , id_r = max( 0 , r - C + 1 );
        rep(j,0,b[i].c) ans[b[i].id] = max( ans[b[i].id] , dp[0][id_l][j] + dp[1][id_r][b[i].c - j] );
    }
    return;
}
void work() {
    int l,r,L,R,ll,rr,last_l,last_r;
    l=1;r=0;ll=1;rr=0;
    int T = 1-p;
    while(T <= b[q].t-p+1) {
    	T += p;
        L = T-p+1; R = T+p-1; //????????? [T-p+1~T+p-1]
        while(a[l].t < L && l <= n) l++;
        while(a[r+1].t <= T && r < n) r++;  //????? [L,T]
        while( b[ll].t < L && ll <= q ) ll++;
        while( b[rr+1].t <= R && rr < q ) rr++; //host [L,R]
        int v_max=0;   rep(i,ll,rr) v_max = max(v_max,b[i].c);
        repp(i,r,l) {
            int id = r-i+1;
            repp(j,v_max,a[i].c) 
                dp[0][id][j] = max( dp[0][id-1][j] , dp[0][id-1][j-a[i].c] + a[i].v );
            repp(j,a[i].c-1,0) dp[0][id][j] = dp[0][id-1][j];
        }//??dp
        last_l = l; last_r = r;
        while(a[l].t <= T && l <= n) l++;
        while(a[r+1].t <= R && r < n) r++; //????? (T,R]
        rep(i,l,r) {
            int id = i-l+1;
            repp(j,v_max,a[i].c)
                dp[1][id][j] = max( dp[1][id-1][j] , dp[1][id-1][j-a[i].c] + a[i].v );
            repp(j,a[i].c-1,0) dp[1][id][j] = dp[1][id-1][j];
        }//??dp
        merge(last_l,last_r,l,r,ll,rr);
        rep(i,1,last_r-last_l+1) rep(j,0,v_max) dp[0][i][j] = 0;
        rep(i,1,r-l+1) rep(j,0,v_max) dp[1][i][j] = 0;
        l = last_r+1; r = last_r; ll = rr+1;
    }
    rep(i,1,q) printf("%d\n",ans[i]);
}
int main() {
    init();
    work();
	return 0; 
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值