尝试用deque写一下单调队列,发现速度还是可以接受的,STL依赖症越来越严重了。。。。
HDU 3415 Max Sum of Max-K-sub-sequence
题意:给出一个有N个数字(-1000..1000,N<=10^5)的环状序列,让你求一个和最大的连续子序列。这个连续子序列的长度小于等于K。
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
#define upmax(a,b) ((a)=(a)>(b)?(a):(b))
const int N=100005;
int data[N];
int sum[2*N],n,k;
deque <int> Q;
void In (int i)
{//以i为开头的一个区间需要比较和保存i-1
while (!Q.empty() && sum[Q.back()]>=sum[i-1]) //>也可
Q.pop_back();
Q.push_back(i-1); //记录该元素的前一个下标
}
void Out (int i)
{
while (!Q.empty() && i-Q.front()>k )
Q.pop_front();
}
int main ()
{
int T;
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&k);
int i;
if (Q.empty()==false) Q.clear();
memset(sum,0,sizeof(sum));
for (i=1;i<=n;i++)
{
scanf("%d",&data[i]);
sum[i]=sum[i-1]+data[i];
}
for (;i<=2*n;i++)
sum[i]=sum[i-1]+data[i-n];
int s=0,e=0,ans=-9999999;
for (i=1;i<=n+k;i++)
{
In(i);
Out(i);
if (ans<sum[i]-sum[Q.front()])
{
upmax(ans,sum[i]-sum[Q.front()]);
s=Q.front()+1;
e=i;
}
}
if (s>n)
s=s-n;
if (e>n)
e=e-n;
printf("%d %d %d\n",ans,s,e);
}
return 0;
}
/*
6
6 6
0 1 2 3 4 5
6 6
1 2 3 -4 5 6
6 6
2 0 -4 5 6 7
8 8
0 1 2 2 1 0 0 0
51 34
-537 -622 -109 302 420 -955 987 570 138 -7 866 -291 -919 -746 -501 -814 -182 121 174 -919 181 -759 -879 514 222 -392 -452 -581 -150 -278 880 911 202 -401 667 123 109 -296 -961 -761 450 716 355 604 -449 -50 -572 644 -364 339 -475
5 5
-1 -1 -1 -1 0
Out
15 2 6
17 5 3
20 4 1
6 2 5
2724 31 11
0 5 5
*/
#include <cstdio>
#include <cstring>
#define upmax(a,b) ((a)=(a)>(b)?(a):(b))
const int N=100005;
int data[N],Q[2*N];
int sum[2*N],n,k,head,tail;
void In (int i)
{//以i为开头的一个区间需要比较和保存i-1
while (head<=tail && sum[Q[tail]]>=sum[i-1]) //>也可
tail--;
Q[++tail]=i-1; //记录该元素的前一个下标
}
void Out (int i)
{
while (head<=tail && i-Q[head]>k)
head++;
}
int main ()
{
int T;
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&k);
int i;
memset(sum,0,sizeof(sum));
for (i=1;i<=n;i++)
{
scanf("%d",&data[i]);
sum[i]=sum[i-1]+data[i];
}
for (;i<=2*n;i++)
sum[i]=sum[i-1]+data[i-n];
head=0;tail=-1;
int s=0,e=0,ans=-9999999;
for (i=1;i<=n+k;i++)
{
In(i);
Out(i);
if (ans<sum[i]-sum[Q[head]])
{
upmax(ans,sum[i]-sum[Q[head]]);
s=Q[head]+1;
e=i;
}
}
if (s>n)
s=s-n;
if (e>n)
e=e-n;
printf("%d %d %d\n",ans,s,e);
}
return 0;
}
Poj 4002 & Hdu 4122 Alice's mooncake shop
题意:一个生产月饼的工厂,给出一个数m,该工厂只在前m小时(也就是[1,m])生产月饼。给出一系列订单,订单给出在第i小时买家要拿走R数量的月饼(1<=i<=m)。生产一个月饼的单价每天不同。工厂有一个冰箱,可以将提前生产的月饼放在冰箱里(工厂也可以在订单到来的那个时刻生产,生产月饼可以瞬间完成),但是放在冰箱里的时间不能超过T。每个月饼在冰箱里保存一天需要额外的费用S。制定工厂的生产计划使得总花费最少?冰箱无限大,每小时生产月饼数量没有限制。
思路:维护一个单调队列,把时间都转化成从2000年第一个月第一天的0时开始
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int N=100005;
int n,m,S,T,p[N]; //p数组表示该时刻的成本
int hour[2505]; //每项请求所在的时间,以小时表示
int R[2505];
char map[12][4]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
int M[2][13]=
{
{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}
};
int Y[]={365,366};
int getMonth (char s[])
{
int i;
for (i=0;i<12;i++)
if (strcmp(map[i],s) == 0)
break;
return i+1;
}
int Is_Year (int x)
{//闰年返回1,否则返回0
return x%400==0 || x%100 && x%4==0;
}
void Input ()
{
char str[15];
int day,year,H,i,j;
for (i=1;i<=n;i++)
{
scanf("%s %d %d %d %d",str,&day,&year,&H,&R[i]);
int month=getMonth(str);
int ans=0;
for (j=2000;j<year;j++)
ans+=Y[Is_Year(j)];
int t=Is_Year(year);
for (int j=1;j<month;j++)
ans+=M[t][j];
ans+=day-1;
hour[i]=ans*24+H+1;
}
scanf("%d%d",&T,&S);
for (i=1;i<=m;i++)
scanf("%d",&p[i]);
}
struct Node
{
int x,y;//x制作时的单价,y制作时的时刻
}tmp;
deque<Node> Q;
int main ()
{
while (scanf("%d%d",&n,&m),n||m)
{
Input ();
Q.clear();
__int64 ans=0;
int head=0,tail=-1;
int id=1;
for (int i=1;i<=m;i++)
{//维护单增队列,队首元素一定单价最小
while (!Q.empty() && Q.back().x+(i-Q.back().y)*S >= p[i])
Q.pop_back();
tmp.x=p[i];
tmp.y=i;
Q.push_back(tmp);
while (id<=n && hour[id]==i)
{
while (!Q.empty() && Q.front().y+T<i) //超出保存期限,删除队头元素
Q.pop_front();
ans+=((__int64)(Q.front().x+(i-Q.front().y)*S))*R[id];
id++;
}
}
printf("%I64d\n",ans);
}
return 0;
}
/*
2 12
Jan 1 2000 9 10
Jan 1 2000 10 10
5 2
20
20
20
10
10
8
7
9
5
10
15
13
0 0
OUT
160
*/
#include <cstdio>
#include <cstring>
#include <cstdlib>
const int N=100005;
int n,m,S,T,p[N]; //p数组表示该时刻的成本
int hour[2505]; //每项请求所在的时间,以小时表示
int R[2505];
char map[12][4]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
int M[2][13]=
{
{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}
};
int Y[]={365,366};
int getMonth (char s[])
{
int i;
for (i=0;i<12;i++)
if (strcmp(map[i],s) == 0)
break;
return i+1;
}
int Is_Year (int x)
{//闰年返回1,否则返回0
return x%400==0 || x%100 && x%4==0;
}
void Input ()
{
char str[15];
int day,year,H,i,j;
for (i=1;i<=n;i++)
{
scanf("%s %d %d %d %d",str,&day,&year,&H,&R[i]);
int month=getMonth(str);
int ans=0;
for (j=2000;j<year;j++)
ans+=Y[Is_Year(j)];
int t=Is_Year(year);
for (int j=1;j<month;j++)
ans+=M[t][j];
ans+=day-1;
hour[i]=ans*24+H+1;
}
scanf("%d%d",&T,&S);
for (i=1;i<=m;i++)
scanf("%d",&p[i]);
}
struct Node
{
int x,y;//x制作时的单价,y制作时的时刻
}Q[N];
int main ()
{
while (scanf("%d%d",&n,&m),n||m)
{
Input ();
__int64 ans=0;
int head=0,tail=-1;
int id=1;
for (int i=1;i<=m;i++)
{//维护单增队列,队首元素一定单价最小
while (head<=tail && Q[tail].x+(i-Q[tail].y)*S >= p[i])
tail--;
tail++;
Q[tail].x=p[i];
Q[tail].y=i;
while (id<=n && hour[id]==i)
{
while (head<tail && Q[head].y+T<i) //超出保存期限,删除队头元素
head++;
ans+=((__int64)(Q[head].x+(i-Q[head].y)*S))*R[id];
id++;
}
}
printf("%I64d\n",ans);
}
return 0;
}
在这里Hdu4193 Non-negative Partial Sums (单调队列) - whyorwhnt的专栏 - 博客频道 - CSDN.NET
已经总结过一次了,这里补一个deque写法
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int N=1000005;
int data[N],sum[2*N];
int n,ans;
deque<int> Q;
void In (int i)
{//单调递增队列,保证队首最小
while (!Q.empty() && sum[Q.back()]>=sum[i])
Q.pop_back();
Q.push_back(i);
}
void Out (int i)
{
if (Q.front()<=i-n) Q.pop_front();
if (sum[Q.front()]-sum[i-n]>=0) //区间内最小的元素>=区间第一个元素
ans++;
}
int main ()
{
while (scanf("%d",&n),n)
{
Q.clear();
ans=0;
memset(sum,0,sizeof(sum));
int i;
for (i=1;i<=n;i++)
{
scanf("%d",&data[i]);
sum[i]+=sum[i-1]+data[i];
}
for (;i<2*n;i++)
sum[i]+=sum[i-1]+data[i-n];
for (i=1;i<n;i++)
In (i);
for (i=n;i<2*n;i++)
{
In(i);
Out(i);
}
printf("%d\n",ans);
}
return 0;
}
合并果子
题目链接:https://vijos.org/p/1097
以下分析摘自:http://www.cnblogs.com/neverforget/archive/2011/10/13/ll.html
这个题目非常的经典,方法也很多,可以采用快排或者堆,其思想都是选取当前最小的两个堆进行合并。复杂度均为O(nlogn),如果用有序队列维护,时间复杂度为O(n)。
每次选取进行合并的两堆,不是最先给定的堆,就是合并最初堆若干次后得到的新堆,所以需要维护两个单调递增队列,一个队列存最初给定的堆的值(1),一个存合并后得到的新值(2)。
每次选择时有三种状态:
1.选取队一的队首两个
2.选取队2的的队首两个
3.选取二者队首各一个
只需对每个队列的指针做相应的更改。
特别注意初始化。
这道题很好的运用了题目中决策的单调性,对初始对经行排序,保证了其单调性。而对于新产生的堆来说,一旦有新元素加入其中,则新元素一定大于原有元素。(很显然,由于队列1的单调性)。
也就是说,队列的单调性是自然而然的。是不需要维护的。要善于观察分析,才能发现。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=10005;
int data[N],Q[N];
int main ()
{
int n;
while (~scanf("%d",&n))
{
int i,front,head,tail;
memset(Q,0x3f3f3f3f,sizeof(Q));
memset(data,0x3f3f3f3f,sizeof(data));
for (i=0;i<n;i++)
scanf("%d",&data[i]);
if (n==1)
{
printf("%d\n",data[0]);
continue;
}
sort(data,data+n);
head=tail=front=0;
int sum=0;
for (i=1;i<=n-1;i++)
{
int tmp=0;
if (data[front]<Q[head])
{
tmp+=data[front];
front++;
}
else
{
tmp+=Q[head];
head++;
}
if (data[front]<Q[head])
{
tmp+=data[front];
front++;
}
else
{
tmp+=Q[head];
head++;
}
sum+=tmp;
Q[tail]=tmp;
tail++;
}
printf("%d\n",sum);
}
return 0;
}