[CF815C] Karen and Supermarket

在回家的路上,凯伦决定到超市停下来买一些杂货。 她需要买很多东西,但因为她是学生,所以她的预算仍然很有限。

事实上,她只花了b美元。

超市出售N种商品。第i件商品可以以ci美元的价格购买。当然,每件商品只能买一次。

最近,超市一直在努力促销。凯伦作为一个忠实的客户,收到了n张优惠券。

如果凯伦购买i次商品,她可以用i次优惠券降低di美元。 当然,不买对应的商品,优惠券不能使用。

然而,对于优惠券有一个规则。对于所有i>=2,为了使用i张优惠券,凯伦必须也使用第xi张优惠券 (这可能意味着使用更多优惠券来满足需求。)

凯伦想知道。她能在不超过预算B的情况下购买的最大商品数量是多少?
输入输出样例
输入样例#1: 复制

6 16
10 9
10 5 1
12 2 1
20 18 3
10 2 3
2 1 5

输出样例#1: 复制

4

输入样例#2: 复制

5 10
3 1
3 1 1
3 1 2
3 1 3
3 1 4

输出样例#2: 复制

5
数据范围:1<=n<=5000

Solution

今天考试考了这道题,我也是醉了,一开始就在想这题,一个小时后调出来了,自信AC,稳了。我还手输了几组数据都过了,详细请看我理解下的题目。后来才发现是i用了优惠,j才能用优惠,我认为是选了i,j就能优惠,关键是样例还过了,哎!还是太菜了。
每个结点只能有一个父节点,容易想到建树,然后树性DP,n=5000可以想到O(\(n^2\))的算法,我们定义\(f[i][j][2]\)代表i结点,选了j个结点,包括它自己,最后一维代表当前结点是否优惠。
容易想到转移方程为
to为x子节点,k代表除to子树外选了k个,j代表to子树选了j个

\[f[x][k+j][0]=min(f[x][k+j][0],f[to][j][0]+f[x][k][0]); \]

\[f[x][k+j][1]=min(f[x][k+j][1],f[to][j][0]+f[x][k][1]); \]

\[f[x][k+j][1]=min(f[x][k+j][1],f[to][j][1]+f[x][k][1]); \]

注意:数组不要赋值0x5f或0x7f,这样两个加起来就爆int了。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct node
{
    int to,next;
}a[1000100];
int w[101000],v[100100],len,last[101010],son[100010],tot;
int f[5100][5100][2];
void add(int a1,int a2)
{
    a[++len].to=a2;
    a[len].next=last[a1];
    last[a1]=len;
}
void dp(int x,int father)
{
    son[x]=1;
    for(int i=last[x];i;i=a[i].next)
    {
        int to=a[i].to;
        if(to==father)
        continue;
        dp(to,x);
        for(int k=son[x];k>=0;k--)
        for(int j=son[to];j>=0;j--)
        {
            f[x][k+j][0]=min(f[x][k+j][0],f[to][j][0]+f[x][k][0]);
            f[x][k+j][1]=min(f[x][k+j][1],min(f[to][j][1],f[to][j][0])+f[x][k][1]);
        }
        son[x]+=son[to];
    }
}
int main()
{
    //freopen("shopping.in","r",stdin);
    //freopen("shopping.out","w",stdout);
    memset(f,0x3f,sizeof(f));
    int n,s,x;
    cin>>n>>s;
    cin>>w[1]>>v[1];
    w[1]-=v[1];
    f[1][0][0]=0;f[1][1][1]=w[1];f[1][1][0]=w[1];
    for(int i=2;i<=n;i++)
    {
        scanf("%d%d%d",&w[i],&v[i],&x);
        add(x,i);add(i,x);
    }
    for(int i=2;i<=n;i++)
    f[i][0][0]=0,f[i][1][1]=w[i]-v[i],f[i][1][0]=w[i];
    dp(1,0);
    for(int i=n;i>=0;i--)
    {
        if(f[1][i][0]<=s||f[1][i][1]<=s)
        {cout<<i;return 0;}
    }
}

博主蒟蒻,可以随意转载,但必须附上原文链接k-z-j

posted @ 2018-08-15 19:51  k-z-j  阅读(211)  评论(0编辑  收藏  举报