Hdu 5188 zhx and contest(暴搜+剪枝or暴力枚举or背包)

题目链接

zhx and contest

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 546    Accepted Submission(s): 192


Problem Description
As one of the most powerful brushes in the world, zhx usually takes part in all kinds of contests.
One day, zhx takes part in an contest. He found the contest very easy for him.
There are  n  problems in the contest. He knows that he can solve the  ith  problem in  ti  units of time and he can get  vi  points.
As he is too powerful, the administrator is watching him. If he finishes the  ith  problem before time  li , he will be considered to cheat.
zhx doesn't really want to solve all these boring problems. He only wants to get no less than  w  points. You are supposed to tell him the minimal time he needs to spend while not being considered to cheat, or he is not able to get enough points. 
Note that zhx can solve only one problem at the same time. And if he starts, he would keep working on it until it is solved. And then he submits his code in no time.
 

Input
Multiply test cases(less than  50 ). Seek  EOF  as the end of the file.
For each test, there are two integers  n  and  w  separated by a space. ( 1n30 0w109 )
Then come n lines which contain three integers  ti,vi,li . ( 1ti,li105,1vi109 )
 

Output
For each test case, output a single line indicating the answer. If zhx is able to get enough points, output the minimal time it takes. Otherwise, output a single line saying "zhx is naive!" (Do not output quotation marks).
 

Sample Input
   
   
   
   
1 3 1 4 7 3 6 4 1 8 6 8 10 1 5 2 2 7 10 4 1 10 2 3
 

Sample Output
   
   
   
   
7 8 zhx is naive!

题意:点击打开链接

题解:将题目按(ti-li)从小到大排序,再按照这个顺序做题目显然是最优的。这样我们就解决了做题顺序的问题。接下若不考虑数据范围,该问题用背包、状态dp都能解决。由于n<=30,暴力枚举的复杂度是2^30。所以我想到用暴搜+剪枝的方法解决这题。

对于剪枝有两种:

最优性剪枝:如果当前的时间超过当前的最优解,则减掉

可行性剪枝:如果当前的得分+剩下的题目的总得分小于要求的得分,则减掉。

代码如下:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<stdlib.h>
#include<vector>
#define inff 0x3fffffff
#define nn 110000
#define mod 1000000007
typedef __int64 LL;
typedef unsigned __int64 LLU;
const LL inf64=inff*(LL)inff;
using namespace std;
int n,w;
struct node
{
    LL t,v,l;
}a[50];
LL sum[50];
LL ans;
bool cmp(node x,node y)
{
    if(x.l-x.t<y.l-y.t)
        return true;
    else if(x.l-x.t==y.l-y.t)
    {
        return x.v>y.v;
    }
    return false;
}
void dfs(int id,LL ti,LL sc)
{
    if(sc>=w)
    {
        ans=min(ans,ti);
        return ;
    }
    if(id==n+1)
        return ;
    if(ti>ans)
        return ;
    if(sum[n]-sum[id-1]+sc<w)
        return ;
    LL ix=ti+a[id].t;
    ix=max(ix,a[id].l);
    dfs(id+1,ix,sc+a[id].v);
    dfs(id+1,ti,sc);
}
int main()
{
    int i;
    while(scanf("%d%d",&n,&w)!=EOF)
    {
        LL ix=0;
        for(i=1;i<=n;i++)
        {
            scanf("%I64d%I64d%I64d",&a[i].t,&a[i].v,&a[i].l);
            ix+=a[i].v;
        }
        if(ix<w)
        {
            puts("zhx is naive!");
            continue;
        }
        sort(a+1,a+n+1,cmp);
        sum[0]=0;
        for(i=1;i<=n;i++)
        {
            sum[i]=sum[i-1]+a[i].v;
        }
        ans=inf64;
        dfs(1,0,0);
        printf("%I64d\n",ans);
    }
    return 0;
}

方法二:暴力枚举(BC官方题解的方法)

当然还是要按(l-t)把题目排序,解决做题顺序的问题。如果直接暴力枚举复杂度是O(2^30)。

我们可以把题目分成两份。按照排好的顺序前一半分成一份,后一半分成一份。

首先暴力枚举前一半题目,求出做前一半题目的所有可能得分即对应的时间。

以得分为优先从小到大排序,从前往后扫一遍处理出要至少得到该分要花的最少时间。

然后在暴力枚举后一半题目,求出相应的得分,并记录做了哪些题。然后再二分查找前一半至少要花多少时间才能使总分至少得到w,然后再求出最后要花多少时间即可。

复杂度O(2^15 * 15)

总结:如果直接暴力枚举要超时,可以尝试把枚举集合分成两部分(可能多部分),分别枚举,在合并得到最终答案。

代码如下:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<stdlib.h>
#include<vector>
#define inff 0x3fffffff
#define nn 110000
#define mod 1000000007
typedef __int64 LL;
typedef unsigned __int64 LLU;
const LL inf64=inff*(LL)inff;
using namespace std;
int n,w;
struct node
{
    LL t,v,l;
}a[50];
LL b[50];
LL ans;
vector<pair<LL,LL> >ve;
bool cmp(node x,node y)
{
    if(x.l-x.t<y.l-y.t)
        return true;
    else if(x.l-x.t==y.l-y.t)
    {
        return x.v>y.v;
    }
    return false;
}
void dfs(int id,LL ti,LL sc)
{
    if(id>n/2)
    {
        if(sc>=w)
        {
            ans=min(ans,ti);
            return ;
        }
        ve.push_back(make_pair(sc,ti));
        return ;
    }
    LL ix=ti+a[id].t;
    ix=max(ix,a[id].l);
    dfs(id+1,ix,sc+a[id].v);
    dfs(id+1,ti,sc);
}
LL jie(LL x)
{
    if((int)ve.size()==0)
        return inf64;
    int l=0,r=ve.size()-1;
    int mid;
    while(l<r)
    {
        mid=(l+r)/2;
        if(ve[mid].first<x)
            l=mid+1;
        else
            r=mid;
    }
    if(ve[l].first<x)
        return inf64;
    return ve[l].second;
}
LL fuck[50],lf;
void df(int id,LL ti,LL sc)
{
    if(id>n)
    {
        if(sc>=w)
        {
            ans=min(ans,ti);
            return ;
        }
        LL ix=jie(w-sc);
        if(ix==inf64)
            return ;
        LL tem=ix;
        for(int i=1;i<=lf;i++)
        {
            tem=max(tem+a[b[i]].t,a[b[i]].l);
        }
        ans=min(ans,tem);
        return ;
    }
    b[++lf]=id;
    df(id+1,max(ti+a[id].t,a[id].l),sc+a[id].v);
    lf--;
    df(id+1,ti,sc);
}
int main()
{
    int i;
    while(scanf("%d%d",&n,&w)!=EOF)
    {
        LL ix=0;
        for(i=1;i<=n;i++)
        {
            scanf("%I64d%I64d%I64d",&a[i].t,&a[i].v,&a[i].l);
            ix+=a[i].v;
        }
        if(ix<w)
        {
            puts("zhx is naive!");
            continue;
        }
        sort(a+1,a+n+1,cmp);
        ans=inf64;
        ve.clear();
        dfs(1,0,0);
        sort(ve.begin(),ve.end());
        int lv=ve.size();
        ix=inf64;
        for(i=lv-1;i>=0;i--)
        {
            ix=min(ix,ve[i].second);
            ve[i].second=ix;
        }
        lf=0;
        df(n/2+1,0,0);
        printf("%I64d\n",ans);
    }
    return 0;
}

方法3:题目按照(l-t)排序后,显然问题就是个背包问题。但是明显空间开不下,但是在背包的过程中其实有很多空间是无效的,没有用到的,所以我们可以用map来解决空间不够的问题,代码如下:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<stdlib.h>
#include<vector>
#define inff 0x3fffffff
#define nn 1100000
#define mod 1000000007
typedef __int64 LL;
typedef unsigned __int64 LLU;
const LL inf64=inff*(LL)inff;
using namespace std;
int n,w;
struct node
{
    LL t,v,l;
}a[50];
LL ans;
map<LL,LL>ma;
map<LL,LL>::iterator it;
bool cmp(node x,node y)
{
    return x.l-x.t<y.l-y.t;
}
int main()
{
    int i;
    while(scanf("%d%d",&n,&w)!=EOF)
    {
        LL ix=0;
        for(i=1;i<=n;i++)
        {
            scanf("%I64d%I64d%I64d",&a[i].t,&a[i].v,&a[i].l);
            ix+=a[i].v;
        }
        if(ix<w)
        {
            puts("zhx is naive!");
            continue;
        }
        sort(a+1,a+n+1,cmp);
        ans=inf64;
        ma.clear();
        ma[0]=0;
        LL fc;
        for(i=1;i<=n;i++)
        {
            it=ma.end();
            for(it--;;it--)
            {
                fc=max(a[i].l,it->first+a[i].t);
                if(ma.count(fc)==0)
                {
                    ma[fc]=it->second+a[i].v;
                }
                else
                    ma[fc]=max(ma[fc],it->second+a[i].v);
                if(ma[fc]>=w)
                    ans=min(ans,fc);
                if(it==ma.begin())
                    break;
            }
        }
        printf("%I64d\n",ans);
    }
    return 0;
}


你可能感兴趣的:(搜索,背包,暴力枚举)