题意
高二数学《绿色通道》总共有 $n$ 道题目要抄,编号 $1\dots n$,抄第 $i$ 题要花 $a_i$ 分钟。小 Y 决定只用不超过 $t$ 分钟抄这个,因此必然有空着的题。每道题要么不写,要么抄完,不能写一半。下标连续的一些空题称为一个空题段,它的长度就是所包含的题目数。这样应付自然会引起马老师的愤怒,最长的空题段越长,马老师越生气。 现在,小 Y 想知道他在这 $t$ 分钟内写哪些题,才能够尽量减轻马老师的怒火。由于小 Y 很聪明,你只要告诉他最长的空题段至少有多长就可以了,不需输出方案。
思路
很明显这道题可以使用二分来寻找最短的最长空题段。(有点绕口)
90分做法
思路
然而发现这道题的数据: 对于所有数据,$0<n \le 5\times 10^4,0<a_i\le 3000,0<t\le 10^8$ 所以直接二分+check(dp)肯定是不行的。 这样复杂度达到了$O(logN \times N^2)$ 但是这样能拿90分(逃: 在使用dp check时: 设$f[i]$表示前i道题目的最少时间。 那么:$f[i]=min(f[i],f[j]+a[i])$且$max(0,i-t-1)\leq j \leq i-1$
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| #include<algorithm> #include<bitset> #include<complex> #include<deque> #include<exception> #include<fstream> #include<functional> #include<iomanip> #include<ios> #include<iosfwd> #include<iostream> #include<istream> #include<iterator> #include<limits> #include<list> #include<locale> #include<map> #include<memory> #include<new> #include<numeric> #include<ostream> #include<queue> #include<set> #include<sstream> #include<stack> #include<stdexcept> #include<streambuf> #include<string> #include<typeinfo> #include<utility> #include<valarray> #include<vector> #include<cctype> #include<cerrno> #include<cfloat> #include<ciso646> #include<climits> #include<clocale> #include<cmath> #include<csetjmp> #include<csignal> #include<cstdarg> #include<cstddef> #include<cstdio> #include<cstdlib> #include<cstring> #include<ctime> using namespace std; inline int read(){ int res=0,f=1;char ch=getchar(); while(ch<'0'ch>'9'){if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar(); return res*f; } inline void write(int x){ if(x<0) putchar('-'),x=-x; if(x<10) putchar(x+'0'); else{ write(x/10); putchar(x%10+'0'); } } int n,t,a[50010],f[50010],l,r,ans; bool check(int x){ memset(f,63,sizeof(f));f[0]=0; for(int i=1;i<=n;i++){ for(int j=max(0,i-x-1);j<=i-1;j++){ f[i]=min(f[i],f[j]+a[i]); } } for(int i=n-x;i<=n;i++) if(f[i]<=t) return 1; return 0; } int main(){ n=read();t=read(); for(int i=1;i<=n;i++) a[i]=read(); l=1;r=n; while(l<=r){ int mid=(l+r)>>1; if(check(mid)==1) ans=mid,r=mid-1; else l=mid+1; } write(ans);putchar('\n'); return 0; }
|
100分做法
思路
与90分做法差不多,只是把check的dp进行单调队列优化:
1 2 3 4
| while(h<=t&&q[h]<max(0,i-x-1)) h++;//如果j不在规定范围内 f[i]=min(f[i],f[q[h]]+a[i]);//取得最小值 while(h<=t&&f[q[t]]>f[i]) t--;//比较大小 q[++t]=i;
|
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| #include<algorithm> #include<bitset> #include<complex> #include<deque> #include<exception> #include<fstream> #include<functional> #include<iomanip> #include<ios> #include<iosfwd> #include<iostream> #include<istream> #include<iterator> #include<limits> #include<list> #include<locale> #include<map> #include<memory> #include<new> #include<numeric> #include<ostream> #include<queue> #include<set> #include<sstream> #include<stack> #include<stdexcept> #include<streambuf> #include<string> #include<typeinfo> #include<utility> #include<valarray> #include<vector> #include<cctype> #include<cerrno> #include<cfloat> #include<ciso646> #include<climits> #include<clocale> #include<cmath> #include<csetjmp> #include<csignal> #include<cstdarg> #include<cstddef> #include<cstdio> #include<cstdlib> #include<cstring> #include<ctime> using namespace std; inline int read(){ int res=0,f=1;char ch=getchar(); while(ch<'0'ch>'9'){if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar(); return res*f; } inline void write(int x){ if(x<0) putchar('-'),x=-x; if(x<10) putchar(x+'0'); else{ write(x/10); putchar(x%10+'0'); } } int n,txx,a[50010],f[50010],l,r,ans,h,t,q[50010]; bool check(int x){ memset(f,63,sizeof(f));f[0]=0; memset(q,0,sizeof(q));h=t=1; for(int i=1;i<=n;i++){ while(h<=t&&q[h]<max(0,i-x-1)) h++; f[i]=min(f[i],f[q[h]]+a[i]); while(h<=t&&f[q[t]]>f[i]) t--; q[++t]=i; } for(int i=n-x;i<=n;i++) if(f[i]<=txx) return 1; return 0; } int main(){ n=read();txx=read(); for(int i=1;i<=n;i++) a[i]=read(); l=1;r=n; while(l<=r){ int mid=(l+r)>>1; if(check(mid)==1) ans=mid,r=mid-1; else l=mid+1; } write(ans);putchar('\n'); return 0; }
|