单调队列优化dp题目
附一链接,大多题型里面有,再附两题:https://blog.csdn.net/hjf1201/article/details/78729320
1.绿色通道
题目描述 Description
《思远高考绿色通道》(Green Passage, GP)是唐山一中常用的练习册之一,其题量之大深受lsz等许多oiers的痛恨,其中又以数学绿色通道为最。2007年某月某日,soon-if (数学课代表),又一次宣布收这本作业,而lsz还一点也没有写……
高二数学《绿色通道》总共有n道题目要写(其实是抄),编号1..n,抄每道题所花时间不一样,抄第i题要花a[i]分钟。由于lsz还要准备NOIP,显然不能成天写绿色通道。lsz决定只用不超过t分钟时间抄这个,因此必然有空着的题。每道题要么不写,要么抄完,不能写一半。一段连续的空题称为一个空题段,它的长度就是所包含的题目数。这样应付自然会引起马老师的愤怒。马老师发怒的程度(简称发怒度)等于最长的空题段长度。
现在,lsz想知道他在这t分钟内写哪些题,才能够尽量降低马老师的发怒度。由于lsz很聪明,你只要告诉他发怒度的数值就可以了,不需输出方案。(快乐融化:那么lsz怎么不自己写程序?lsz:我还在抄别的科目的作业……)
输入描述 Input Description
第一行为两个整数n,t,代表共有n道题目,t分钟时间。
以下一行,为n个整数,依次为a[1], a[2],… a[n],意义如上所述。
输出描述 Output Description
仅一行,一个整数w,为最低的发怒度。
样例输入 Sample Input
17 11
6 4 5 2 5 3 4 5 2 3 4 5 2 3 6 3 5
样例输出 Sample Output
3
数据范围及提示 Data Size & Hint
60%数据 n<=2000
100%数据 0 < n <=50000,0 < a[i] <=3000,0< t<=100000000
二分+dp+单调队列优化(优先队列也行)
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,t,a[50100],ans,sum,dp[50100]; 4 int he=1,ta; 5 struct edge{ 6 int data,id; 7 }q[50100]; 8 9 inline int read(){ 10 int x=0,f=1;char ch=getchar(); 11 while(ch<'0'||ch>'9') { if(ch=='-') f=-1;ch=getchar();} 12 while(ch>='0'&&ch<='9') { x=x*10+ch-'0';ch=getchar();} 13 return x*f; 14 } 15 16 inline bool check(int x,int tim){ 17 memset(dp,0x3f3f3f3f,sizeof(dp)); 18 he=1;ta=0;q[++ta].data=q[ta].id=0; 19 dp[0]=0; 20 for(int i=1;i<=n;i++){ 21 while(i-q[he].id-1>x) he++; 22 dp[i]=q[he].data+a[i]; 23 while(q[ta].data>dp[i]) ta--; 24 q[++ta].data=dp[i];q[ta].id=i; 25 } 26 for(int i=n-1;i>=n-x;i--) 27 dp[n]=min(dp[n],dp[i]); 28 if(dp[n]<=tim) return true; 29 else return false; 30 } 31 32 int main(){ 33 n=read();t=read(); 34 for(int i=1;i<=n;i++) a[i]=read(),sum+=a[i]; 35 if(sum<=t){ 36 printf("0\n"); 37 return 0; 38 } 39 int l=1,r=n; 40 while(l<=r){ 41 int mid=(l+r)>>1; 42 if(check(mid,t)) ans=mid,r=mid-1; 43 else l=mid+1; 44 } 45 printf("%d\n",ans); 46 return 0; 47 }
2.czy的后宫6
题目描述
众所周知的是丧尸czy有很多妹子(虽然很多但是质量不容乐观QAQ),今天czy把n个妹子排成一行来检阅。但是czy的妹子的质量实在……所以czy看不下去了。检阅了第i个妹子会增加czy a[i]的肾虚值,他打算在检阅过程中最多休息m次(一开始检阅算0次休息,就是说czy最多可以检阅m+1次),每次休息过后czy又会龙精虎猛的继续检阅。问怎样分配才能使得czy在检阅过程中的最大肾虚值最小。
当然这么简单的问题czy早就会做啦……他原来还想算算满足肾虚值最小的条件下有几种方案,但是他太虚了,所以这个问题也交给你啦。你只要输出方案数mod 32123的值即可。
输入格式
第一行输入两个正整数n、m,表示czy的妹子数、最多的休息次数
接下来2到n+1行每行输入一个数a[i],意义见上
输出格式
第一行输出一个数s,表示最小的肾虚值
第二行输出一个数t,表示方案数
样例输入
4 2
3
4
5
2
样例输出
7
3
样例解释
最小的肾虚值为7
分法有3种:34|5|2,34|52,3|4|52
‘|’表示休息
数据范围
有30%的数据,1<=n<=20
另30%的数据,1<=n<=200
另30%的数据,1<=n<=5000,1<=m<=min(n-1,1000),1<=a[i]<=1000
另10%的数据,1<=n<=20000,1<=m<=1000,a[i]只有1、2
保证80%数据随机生成,在计算过程中不会爆int
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define inf 0x7fffffff 4 #define ll long long 5 #define mod 32123 6 #define N 200010 7 using namespace std; 8 9 ll read() 10 { 11 ll x=0,f=1;char ch=getchar(); 12 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 13 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 14 return x*f; 15 } 16 int f[20005][1005],l,r,a[N],n,m,tot[N],ans; 17 bool check(int x) 18 { 19 int tot=0,cnt=0; 20 for (int i=1;i<=n;i++) 21 { 22 tot+=a[i]; 23 if (tot>x) cnt++,tot=a[i]; 24 if (cnt>m) return false; 25 } 26 return true; 27 } 28 int main() 29 { 30 n=read(),m=read(); 31 for (int i=1;i<=n;i++) 32 { 33 a[i]=read(); 34 r+=a[i]; 35 } 36 l=0; 37 while (l<r) 38 { 39 int mid=(l+r)>>1; 40 if (check(mid)) r=mid; 41 else l=mid+1; 42 } 43 int ans1=r; 44 printf("%d\n",ans1); 45 int sum=0,ans2=0; 46 f[0][0]=1;tot[0]=1;//tot:到目前分j段的方案数 47 int l=0; 48 for(int i=1;i<=n;i++) 49 { 50 sum+=a[i]; 51 while(sum-a[l]>ans1) 52 { 53 sum-=a[l];//单调队列优化 54 for(int j=0;j<=m+1;j++) 55 { 56 tot[j]-=f[l][j]; 57 if(tot[j]<0)tot[j]+=mod; 58 } 59 l++; 60 } 61 for(int j=m+1;j>=1;j--) 62 { 63 f[i][j]+=tot[j-1]; 64 tot[j]+=f[i][j]; 65 tot[j]%=mod; 66 f[i][j]%=mod; 67 } 68 } 69 for(int i=0;i<=m+1;i++) 70 ans2=(ans2+f[n][i])%mod; 71 printf("%d",ans2); 72 return 0; 73 }