T1
题意
给出n个点的坐标,以及时间差d。求区间内y坐标的最大值与最小值差大于等于d 的最小x轴区间长度。 (n<=1e5, d<=1e6, x,y<=1e6)
### 思路
设l为一段满足条件的区间的左端点,r为该区间最小右端点,则r具有单调性。
『 单调性证明:
设端点l1满足条件的最小右端点为r1, 端点l2(l2>=l1)满足条件的最小右端点为r2。
则: r2>=r1, 否则端点l1满足条件的最小右端点为r2,矛盾。
故: r具有单调性。 』
因此可在对n个点按x排序之后,枚举左端点,用单调队列维护满足条件的区间的y轴上的最大值与最小值。
代码
#include <bits/stdc++.h> #define I __inline__ __attribute((always_inline)) #define ri register int using namespace std; const int inf = 0x3f3f3f3f; const int maxn = 1e5 + 10; I char readc(){ static char buf[100000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } I int readI( ){ static char c=readc();ri x,f=1; for(;c>'9'||c<'0';c=readc()) if(c=='-') f=-1; for(x=0;c<='9'&&c>='0';c=readc()) x=(x<<3)+(x<<1)+c-48; return x*f; } struct node { int x, y; void read() { x = readI(), y = readI(); } inline bool operator<(const node &p) const { return x < p.x; } } rain[maxn]; int q1[maxn], h1=1, t1; int q2[maxn], h2=1, t2; int n, d; int ans = inf; int main() { n = readI(), d = readI(); for (int i = 1; i <= n; ++i) rain[i].read(); sort(rain + 1, rain + 1 + n); for (register int l = 1, r = 0; l <= n; ++l) { while (h1 <= t2 && q1[h1] < l) ++h1; while (h2 <= t2 && q2[h2] < l) ++h2; while (rain[q1[h1]].y - rain[q2[h2]].y < d && r < n) { ++r; while (h1 <= t1 && rain[q1[t1]].y < rain[r].y) --t1; q1[++t1] = r; while (h2 <= t2 && rain[q2[t2]].y > rain[r].y) --t2; q2[++t2] = r; } if (rain[q1[h1]].y - rain[q2[h2]].y >= d) ans = min(ans, rain[r].x - rain[l].x); } if(ans == inf) ans = -1; printf("%d\n", ans); return 0; }
T2
题意
有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小。(a, b<=1000, n<=100)
思路
首先对于每一行可以用单调队列维护每n个元素的最大值与最小值,并分别存到两个数组中。
然后对于每一列依旧用单调队列分别维护最大值数组每n个元素的最大值与最小值数组中每n个元素的最小值,得到新的最大值与最小值数组。
答案即为新的最大值数组与最小值数组差的最小值。
代码
#include <bits/stdc++.h> #define ri register int using namespace std; const int inf = 0x3f3f3f3f; const int maxn = 1e3 + 10; int q1[maxn], q2[maxn]; int mp[maxn][maxn], a, b, n; int mx[maxn][maxn], mi[maxn][maxn]; int mxy[maxn][maxn], miy[maxn][maxn]; int ans = inf; int main() { // freopen("P2216.in","r", stdin); ri h1 = 1, h2 = 1, t1 = 0, t2 = 0; scanf("%d%d%d", &a, &b, &n); for (ri i = 1; i <= a; ++i) { for (ri j = 1; j <= b; ++j) scanf("%d", &mp[i][j]); } for (ri i = 1; i <= a; ++i) { h1 = h2 = 1; t1 = t2 = 0; for (ri j = 1; j <= b; ++j) { while (h1 <= t1 && q1[h1] <= j - n) ++h1; while (h2 <= t2 && q2[h2] <= j - n) ++h2; while (h1 <= t1 && mp[i][q1[t1]] < mp[i][j]) --t1; q1[++t1] = j; while (h2 <= t2 && mp[i][q2[t2]] > mp[i][j]) --t2; q2[++t2] = j; if (j >= n) mx[i][j] = mp[i][q1[h1]], mi[i][j] = mp[i][q2[h2]]; } } for (ri j = 1; j <= b; ++j) { h1 = h2 = 1; t1 = t2 = 0; for (ri i = 1; i <= a; ++i) { while (h1 <= t1 && q1[h1] <= i - n) ++h1; while (h2 <= t2 && q2[h2] <= i - n) ++h2; while (h1 <= t1 && mx[q1[t1]][j] < mx[i][j]) --t1; q1[++t1] = i; while (h2 <= t2 && mi[q2[t2]][j] > mi[i][j]) --t2; q2[++t2] = i; if (i >= n) mxy[i][j] = mx[q1[h1]][j], miy[i][j] = mi[q2[h2]][j]; } } for (int i = n; i <= a; ++i) { for (int j = n; j <= b; ++j) ans = min(ans, mxy[i][j] - miy[i][j]); } printf("%d\n", ans); return 0; }
T3
题意
在一个n*m的矩形里,框出一个a*b的矩形A,在这个矩形内部(不相交)框出一个c*d的矩形B,使得矩形A的权值和-矩形B的权值和最大,求最大权值和。
1<=M,N<=1000,1<=A<=M,1<=B<=N,1<=C<=A-2,1<=D<=B-2,1<=“肥沃度”<=100
思路
使差最大就 要是被减数尽可能大,减数尽可能小,所以就要用单调队列维护矩形为a*b的最大值,以及单调队列维护矩形为c*d的最小值。
维护时要注意边界条件
代码
#include <bits/stdc++.h> using namespace std; const int maxn = 1e3+10; int n, m, a, b, c, d; int mp[maxn][maxn]; int tmp[maxn][maxn], res[maxn][maxn]; int pref[maxn][maxn], preg[maxn][maxn]; int ans = -1; int que[maxn*maxn], h, t; int main() { // freopen("text.in", "r", stdin); scanf("%d%d%d%d%d%d", &n, &m, &a, &b, &c, &d); for (int i=1; i<=n; ++i) { for (int j=1; j<=m; ++j) { scanf("%d", &mp[i][j]); mp[i][j] += mp[i-1][j] + mp[i][j-1] - mp[i-1][j-1]; } } for (int i=c+1; i<n; ++i) { for (int j=d+1; j<m; ++j) pref[i][j] = mp[i][j] - mp[i-c][j] - mp[i][j-d] + mp[i-c][j-d]; } for (int i=a; i<=n; ++i) { for (int j=b; j<=m; ++j) preg[i][j] = mp[i][j] - mp[i-a][j] - mp[i][j-b] + mp[i-a][j-b]; } for (int i=c+1; i<n; ++i) { h = 1, t = 0; for (int j=d+1; j<m; ++j) { while(h<=t && que[h]<=j-(b-d-2)) ++h; while(h<=t && pref[i][que[t]]>pref[i][j]) --t; que[++t] = j; if(j>=b-1) tmp[i][j+1] = pref[i][que[h]]; } } for (int j=b; j<=m; ++j) { h = 1, t = 0; for (int i=c+1; i<n; ++i) { while(h<=t && que[h]<=i-(a-c-2)) ++h; while(h<=t && tmp[que[t]][j]>tmp[i][j]) --t; que[++t] = i; if(i>=a-1) res[i+1][j] = tmp[que[h]][j]; } } for (int i=a; i<=n; ++i) { for (int j=b; j<=m; ++j) ans = max(ans, preg[i][j] - res[i][j]); } printf("%d\n", ans); return 0; }
T4
题意
有K种珠子共n个,分布在x轴上, 求包含k种珠子的最短线段长度。
1≤N≤1000000,1≤K≤60,0≤珠子位置<2^31
思路
将珠子按x坐标排序后,随着x坐标的增大,种类只增不减,因此可以用单调队列维护种类大于等于k时的最短长度。
『
由于新加入的珠子除可能影响珠子种类数以外,不会对其他珠子产生影响,所以只需考虑队首元素出队的情况即可。
』
代码
#include <bits/stdc++.h> #define ri register int using namespace std; const int maxn = 1e6+10; struct node { int x, kind; inline bool operator<(const node &p) const { return x<p.x; } }ball[maxn]; int n, K, pn; int que[maxn]; int cnt[110], res, ans = INT_MAX; int main() { ri h = 1, t = 0; scanf("%d%d", &n, &K); for (int x, k, i=1; i<=K; ++i) { scanf("%d", &k); while(k--) { scanf("%d", &x); ball[++pn] = node{x, i}; } } sort(ball+1, ball+1+n); for (int i=1; i<=n; ++i) { que[++t] = i; if(!cnt[ball[que[t]].kind]) ++res; ++cnt[ball[que[t]].kind]; while(res==K) { ans = min(ans, ball[que[t]].x - ball[que[h]].x); if(h<=t) { --cnt[ball[que[h]].kind]; if(!cnt[ball[que[h]].kind]) --res; ++h; } } } printf("%d\n", ans); return 0; }
T5
题意
对于某只股票,已知它未来T天的走势,即 买入价ap, 卖出价bp, 买入数量上限as, 卖出数量上限bs。
限制条件: 手中最多p股,相邻的两次交易必须间隔w天。
假设手中有无穷多钱, 0股。
0≤W<T≤2000,1≤MaxP≤2000,1≤bp≤ap≤1000,1≤as,bs≤Maxp
思路
对于第i天有四种情况:
(1) 从0股开始买, f[i][j] = -ap[i]*j;
(2) 啥也不干, f[i][j] = max(f[i][j], f[i-1][j]);
(3) 买入:f[i][j] = max(f[i][j], f[i-w-1][k]-(j-k)*ap[i]);
(4) 卖出:f[i][j] = max(f[i][j], f[i-w-1][k]+(k-j)*bp[i]);
对于(3),(4)暴力转移复杂度O(n^3), 仔细分析转移方程性质: max(f[i-w-1][k]+k*ap[i])-j*ap[i], 对于转移只需要知道最大的k即可,因此可用单调队列维护以下最大值即可。
考虑买入与卖出的关系,(3)需要正序,(4)需要倒序。
### 代码
```cpp
#include <bits/stdc++.h>
define ri register int
using namespace std;
const int maxn = 1e6+10;
struct node {
int x, kind;
inline bool operator<(const node &p) const {
return x<p.x;
}
}ball[maxn];
int n, K, pn;
int que[maxn];
int cnt[110], res, ans = INT_MAX;
int main() {
ri h = 1, t = 0;
scanf("%d%d", &n, &K);
for (int x, k, i=1; i<=K; ++i) {
scanf("%d", &k);
while(k--) {
scanf("%d", &x);
ball[++pn] = node{x, i};
}
}
sort(ball+1, ball+1+n);
for (int i=1; i<=n; ++i) {
que[++t] = i;
if(!cnt[ball[que[t]].kind]) ++res;
++cnt[ball[que[t]].kind];
while(res==K) {
ans = min(ans, ball[que[t]].x - ball[que[h]].x);
if(h<=t) {
--cnt[ball[que[h]].kind];
if(!cnt[ball[que[h]].kind]) --res;
++h;
}
}
}
printf("%d\n", ans);
return 0;
}
```
来源:https://www.cnblogs.com/acerkoo/p/10927368.html