500F New Year Shopping(分而治之+01背包DP)

F. New Year Shopping
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output
Dohyun is running a grocery store. He sells n items numbered by integers from 1 to n. The i-th (1 ≤ i ≤ n) of them costs ci dollars, and if I buy it, my happiness increases by hi. Each item can be displayed only for p units of time because of freshness. As Dohyun displays thei-th item at time ti, the customers can buy the i-th item only from time ti to time ti + (p - 1) inclusively. Also, each customer cannot buy the same item more than once.

I'd like to visit Dohyun's grocery store and buy some items for the New Year Party, and maximize my happiness. Because I am a really busy person, I can visit the store only once, and for very short period of time. In other words, if I visit the store at time t, I can only buy the items available at time t. But I can buy as many items as possible, if the budget holds. I can't buy same item several times due to store rules. It is not necessary to use the whole budget.

I made a list of q pairs of integers (aj, bj), which means I may visit the store at time aj, and spend at most bj dollars at the store. For each pair, I'd like to know the maximum happiness I can obtain. But there are so many pairs that I can't handle them. Can you help me?

Input
The first line contains two space-separated integers n and p (1 ≤ n ≤ 4000, 1 ≤ p ≤ 10 000) — the number of items, and the display time of each item.

Next n lines describe the items. The i-th (1 ≤ i ≤ n) of them contains three space-separated integers ci, hi, ti (1 ≤ ci, hi ≤ 4000, 1 ≤ ti ≤ 10 000) — the cost of the i-th item, the happiness of the i-th item, and the time when the i-th item starts to be displayed.

The next line contains an integer q (1 ≤ q ≤ 20 000)— the number of candidates.

Next q lines describe the candidates. The j-th (1 ≤ j ≤ q) of them contains two space-separated integers aj, bj (1 ≤ aj ≤ 20 000, 1 ≤ bj ≤ 4000) — the visit time and the budget for j-th visit of store.

Output
For each candidate, print a single line containing the maximum happiness that I can obtain by buying some items.

Sample test(s)
input
4 4
2 3 2
3 5 1
4 7 2
11 15 5
4
1 3
2 5
2 6
5 14

output
5
8
10
18

input
5 4
3 2 1
7 4 4
2 1 2
6 3 5
3 2 2
10
1 5
2 5
4 8
4 9
4 10
5 8
5 9
5 10
8 4
7 9

output
2
3
5
5
6
4
5
6
0
4Note
Consider the first sample.


At time 1, only the 2nd item is available. I can buy the 2nd item using 3 dollars and my happiness will increase by 5.
At time 2, the 1st, 2nd, and 3rd item is available. I can buy the 1st item using 2 dollars, and the 2nd item using 3 dollars. My happiness will increase by 3 + 5 = 8.
At time 2, the 1st, 2nd, and 3rd item is available. I can buy the 1st item using 2 dollars, and the 3nd item using 4 dollars. My happiness will increase by 3 + 7 = 10.
At time 5, the 1st, 3rd, and 4th item is available. I can buy the 1st item using 2 dollars, and the 4th item using 11 dollars. My happiness will increase by 3 + 15 = 18. Note that I don't need to use the whole budget in this case.


题意:有n件物品,对于第i个物品, 买物品花费c, 买了开心加h, 每个物品的出现时间t, 每样物品展出时间为k,只能在展出时间购买, 对于q次询问,在时间a, 你最多花费b,最大的开心值。(每样最多买一个)

思路:跟01背包十分相似,但难点是就算你能模拟出当前时间点物品存在,你很难找到一个高效的算法dp,dp之间是有关联的,当一件物品消失了后就要重新进行dp,这样会大大增加耗时导致超时,没思路。。。后来我看到这题的知识点有分而治之,又细细琢磨了一位大神的代码半天,总算明白了这题的巧妙之处,其实就是两个链表维护两个dp数组,比如dp1,dp2。dp1是正向dp的,dp2是反向dp的,两个链表的个数和就是当前时间点存在的物品个数。 

每当要消失物品时,看反向链表有无元素, #1.有的话直接末尾删除,并且反向的dp2数组删除末尾,不需要重新计算,#2.没元素就将正向链表元素反向剪切过来并且重新计算放入dp2中。

每当增加物品时候,只要往正向链表尾端添加元素,正常dp下就行。

这里用了分治的方法能大大减少每次一消失物品就全部重新计算的次数。

代码:(我这里链表用了双端队列实现)

#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<deque>
#include<algorithm>
using namespace std;
struct qut
{
    int a,b,id;
} q[20005];
bool operator<(const qut &x,const qut &y)
{
    return x.a<y.a;
}
const int N = 5000;
int c[N],h[N],t[N],dp1[N][N],dp2[N][N],ans[20005];
vector<int>f[20005];
deque<int>que1,que2;
int main()
{
    int n,k,qn,tt=0,sz1=0,sz2=0,xu;
    scanf("%d%d",&n,&k);
    for(int i=1; i<=n; i++)
    {
        scanf("%d%d%d",&c[i],&h[i],&t[i]);
        tt=max(tt,t[i]);
        f[t[i]].push_back(i);
        f[t[i]+k].push_back(-i);
    }
    scanf("%d",&qn);
    for(int i=0; i<qn; i++)
    {
        scanf("%d%d",&q[i].a,&q[i].b);
        tt=max(tt,q[i].a-k);
        q[i].id=i;
    }
    sort(q,q+qn);
    n=0;
    for(int i=1; i<=tt+k; i++)
    {
        while(f[i].size())
        {
            if(f[i][f[i].size()-1]>0)
            {
                xu=f[i][f[i].size()-1];
                que1.push_back(xu);
                sz1++;
                for(int j=0; j<=4000; j++)
                    dp1[sz1][j]=dp1[sz1-1][j];
                for(int j=c[xu]; j<=4000; j++)
                    dp1[sz1][j]=max(dp1[sz1][j],dp1[sz1-1][j-c[xu]]+h[xu]);
            }
            else
            {
                if(que2.empty()&&sz2==0)
                {
                    for(int u=1; u<=que1.size(); u++)
                    {
                        xu=que1[que1.size()-u];
                        que2.push_back(xu);
                        sz2++,sz1--;
                        for(int j=0; j<=4000; j++)
                            dp2[sz2][j]=dp2[sz2-1][j];
                        for(int j=c[xu]; j<=4000; j++)
                            dp2[sz2][j]=max(dp2[sz2][j],dp2[sz2-1][j-c[xu]]+h[xu]);
                    }
                    while(!que1.empty())que1.pop_back();
                }
                que2.pop_front(),sz2--;
            }
            f[i].pop_back();//vector只有pop_back(),没有pop_front()
        }
        while(q[n].a==i)
        {
            for(int j=0; j<=q[n].b; j++)
                ans[q[n].id]=max(ans[q[n].id],dp1[sz1][j]+dp2[sz2][q[n].b-j]);
            n++;
        }
    }
    for(int i=0; i<n; i++)
        printf("%d\n",ans[i]);
}

 顺便贴下大神的代码把,是用数组实现的,方便简洁

#include<cstdio>
#include<vector>
#include<algorithm>

int a[4001];
int b[4001];
int c[4001];

int s[4001],sn;
int t[4001],tn;

int d[4001][4001];
int e[4001][4001];

std::vector<int> f[20001];
std::pair<std::pair<int,int>,int> q[20000];
int r[20000];

int main()
{
    int i,j,k,n,m;
    scanf("%d%d",&n,&m);
    for(i=1; i<=n; i++)
    {
        scanf("%d%d%d",&a[i],&b[i],&c[i]);
        f[c[i]].push_back(i);
        f[c[i]+m].push_back(-i);
    }
    scanf("%d",&m);
    for(i=0; i<m; i++)
    {
        scanf("%d%d",&q[i].first.first,&q[i].first.second);
        q[i].second=i;
    }
    std::sort(q,q+m);
    n=0;
    for(i=1; i<=20000; i++)
    {
        while(f[i].size())
        {
            if(f[i][f[i].size()-1]>0)
            {
                s[++sn]=f[i][f[i].size()-1];
                for(j=0; j<=4000; j++)
                    d[sn][j]=d[sn-1][j];
                for(j=a[s[sn]]; j<=4000; j++)
                    d[sn][j]=std::max(d[sn][j],d[sn-1][j-a[s[sn]]]+b[s[sn]]);
            }
            else
            {
                if(!tn)
                {
                    while(sn)
                    {
                        t[++tn]=s[sn--];
                        for(j=0; j<=4000; j++)
                            e[tn][j]=e[tn-1][j];
                        for(j=a[t[tn]]; j<=4000; j++)
                            e[tn][j]=std::max(e[tn][j],e[tn-1][j-a[t[tn]]]+b[t[tn]]);
                    }
                }
                tn--;
            }
            f[i].pop_back();
        }
        while(q[n].first.first==i)
        {
            for(j=0; j<=q[n].first.second; j++)
                r[q[n].second]=std::max(r[q[n].second],d[sn][j]+e[tn][q[n].first.second-j]);
            n++;
        }
    }
    for(i=0; i<n; i++)printf("%d\n",r[i]);
}
View Code

 

posted @ 2015-02-04 18:06  Doli  阅读(299)  评论(0)    收藏  举报