题目链接
1 3 1 4 7 3 6 4 1 8 6 8 10 1 5 2 2 7 10 4 1 10 2 3
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; }