tzoj 3847 Mowing the Lawn (单调队列 )
烽火传递:https://blog.csdn.net/magical_qting/article/details/47205093
这篇博客的题和我要写的题思路类似(我把他代码改了下 更好懂
Description
烽火台又称烽燧,是重要的军事防御设施,一般建在险要或交通要道上。一旦有敌情发生,白天燃烧柴草,通过浓烟表达信息;夜晚燃烧干柴,以火光传递军情,在某两座城市之间有n个烽火台,每个烽火台发出信号都有一定代价。为了使情报准确地传递,在连续m个烽火台中至少要有一个发出信号。请计算总共最少花费多少代价,才能使敌军来袭之时,情报能在这两座城市之间准确传递。
Input
第一行:两个整数N,M。其中N表示烽火台的个数,M表示在连续m个烽火台中至少要有一个发出信号。接下来N行,每行一个数Wi,表示第i个烽火台发出信号所需代价。
Sample Input
5 3
1
2
5
6
2
Sample Output
4
思路:
dp加单调队列优化。
f[i]表示当第i个烽火台发出信号,前i个烽火台最少付出的代价。
f[i]=min{f[k]}(i-m<=k<=i-1)+w[i]
单调队列维护一单调递增区间,每次队首取出最小值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> #define N 100010 #define ll long long using namespace std; ll n,m,dp[N],que[N]; int main() { scanf("%I64d%I64d",&n,&m);//20 3 ll head=1,tail=0,sum=0;//head=1,tail=0 que[++tail]=0; for(ll i=1;i<=n;i++) { ll w; scanf("%I64d",&w); while(que[head]<i-m)//超m的出队 head++; dp[i]=w+dp[que[head]];//更新 while(head<=tail&&dp[que[tail]]>=dp[i])//比较 出队 tail--; que[++tail]=i; //入队 } ll minn=1e15; for(ll i=n;i>n-m;i--) //20-17 minn=min(minn,dp[i]); printf("%I64d\n",minn); return 0; }
然后是3847:
题意:可以获得的最大总奶牛效率,而不会选择超过m个连续奶牛
即在m+1中选择一个地方设断点,断点的地方不选 其他选上,求选上的奶牛效率总和最大。
而烽火那题就是在m中选一个断点(要最小),断点选上,其他不选,求选上的断点的min
3847求得就是所有总和 减去烽火那题要求的min
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> #define N 100010 #define ll long long using namespace std; ll n,m,dp[N],que[N]; int main() { scanf("%I64d%I64d",&n,&m);//20 3 m=m+1;//m+1中必有一个断点 ll head=1,tail=0,sum=0;//head=1,tail=0 que[++tail]=0; for(ll i=1;i<=n;i++) { ll w; scanf("%I64d",&w); sum+=w; while(que[head]<i-m)//超m的出队 head++; dp[i]=w+dp[que[head]];//更新 while(head<=tail&&dp[que[tail]]>=dp[i])//比较 出队 tail--; que[++tail]=i; //入队 } ll minn=1e15; for(ll i=n;i>n-m;i--) //20-17 minn=min(minn,dp[i]); printf("%I64d\n",sum-minn); // for(ll i=1;i<=n;i++)cout<<dp[i]<<endl; return 0; }