引子 P5019----铺设道路
题目翻译:现在给定一个长度为n的数组a,每次可以选择一个连续正整数区间进行整体-1,问最少操作几次使得数组全部变成0;
看到这题,我的第一反应是:先找寻一个连续区间里的最小值,那么这个区间整体先减去此最小值,循环往复;
这个思路有些贪心的意味,也确实是对的,通过此题也是可以的。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define pii pair<int ,int >
int n;
int d[120000],a[120000];
int cnt = 0,tian = 0,minn;
queue<pii> q;
int x,y;
int main(){
cin >> n;
x = 0;
y = 1;
for(int i=1; i<=n; i++){
cin >> d[i];
}
for(int i=1; i<=n+1; i++,y++){
if(d[i] == 0){
cnt ++;
q.push({x,y});
//cout << x << " " << y << endl;
x = y;
}
}
//cout << x <<" " << y << endl;
while(q.size()){
int head = q.front().first;
int tail = q.front().second;
x = head; y = head + 1;
//cout << x <<" "<< y << endl;
q.pop();
if(tail - head == 1) continue;
minn = 50000;
for(int i=head+1; i<tail; i++){
minn = min(minn,d[i]);
}
for(int i=head+1; i<=tail; i++,y++){
if(d[i] > 0) d[i] -= minn;
if(d[i] == 0){
cnt++;
q.push({x,y});
//cout << x << " " << y << endl;
x = y;
}
}
tian += minn;
}
cout << tian;
return 0;
}
正解----P1969积木大赛
题意:给定一个全0数组和一个目标数组,每次操作可以选择一个区间整体+1,问最少操作几次变成目标数组?
这道题我的思路依旧是先铺公共部分
为了方便贪心,我创造一个几何情景。
对于一个下标x,输入为y,意思是创造了(x,1),(x,2)...(x,y)点,一段连续区间➕1可以抽象为:在高度为y的地方填充一条覆盖[L,R]的线段。
现在至少画几条线段可以实现完全覆盖?
注意到:铺完公共部分后,距离目标的数值减少了。
假如 5 4 1 8 8
现在整体铺完第一层
即是画一条覆盖[1,5],y=1的线段。
变成 4 3 0 7 7
观察 4 3
他们的公共部分为3,我可以在[1,2]处上下画3条线段覆盖高度y=1、y=2、y=3(相对高度y),但是4这里多了一点,因此我要加上这一个点
答案是 3 + 1 = 4
而 7 7
我一次性在[4,5]处画7条线段铺满 y∈N*,y∈[1,7];(相对高度y)
因此总次数 1 + 3 + 1 + 7 = 12(次)
假如左小右边大呢?
4 3
当遍历到4时,可以先创造四个点(1,1),(1,2),(1,3),(1,4),顺便画四个点覆盖他们;
那么后来的3意思是(2,1),(2,2),(2,3)需要被覆盖
因此我们只需要把原先的三个y=∈[1,3]的点扩画成长度为2的线段就好了,无需再创造一个更高的覆盖不到的点
本题目的解法也就出来了:
计算每个点的差分值x,如果x>0,那么ans +=x;
ans 即是答案;
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
#define ll long long
ll ans=0;
int a[N];
int n;
int main(){
cin >> n;
a[0] = 0;
for(int i=1; i<=n; i++){
cin >> a[i];
int x = a[i] - a[i-1];
if(x >= 0) ans += x;
}
cout << ans;
return 0;
}
三倍经验
P3078 [USACO13MAR] Poker Hands S,转化题意也是这个模型,甚至比我的例子贴近几何情景。
浙公网安备 33010602011771号