【LDUOJ】寒假专题训练——贪心EG题解
星星之火,可以燎原
G题 最佳游览线路
附上一张从洛谷复制来的图片
题意:
对于每一条旅行街(绿色线条),只可以从西边走到东边
而对于绿化道,可以选择从北向南,也可以选择从南向北,方向不限
对于旅行街,每走一段都会获得一个权值(-100到100的整数)
比如说我走下图中紫色箭头指向的这一条段街道,我将会获得一个权值 -42
注意:可以从任一个路口开始游览,在任一个路口结束游览
所有权值不可能全为负
浏览的部分应该是连续的
贪心的策略:
挑选每一列中最大的那个数,选择最大权值的那一段街道进行浏览
但是有个问题,如果说浏览了某一段之后,到目前为止的总和是负数,那么我可以直接抛弃这一段,因为前面的这一段负数可能会降低我最终答案的值,成为最终答案的“累赘”
所以代码也是非常的简单 心不慌,手不抖,代码跟着思路走
提前预处理出每一列的最大值
然后再从这些最大值中选取一段连续的,一直取max
如果加上某个权值之后,导致sum < 0 ,直接放弃前面的一段(前面可能会有的贡献已经被记录在max中)
代码
/*** keep hungry and calm PushyTao!***/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int m,n;
int a[107][20007];
int b[20007];
int main() {
int m,n;
cin >> m >> n;
for(int i=1; i<=m; i++) {
for(int j=1; j<=n; j++) {
scanf("%d",&a[i][j]);
}
}
int sum = 0;
int ans = 0;
for(int i=1; i<=n; i++) {
int mx = -200;
for(int j=1; j<=m; j++) {
mx = max(mx,a[j][i]);
}
b[i] = mx;///每列的最大值
}
for(int i=1;i<=n;i++){
sum += b[i];
if(sum < 0) sum = 0;///如果说加上这个权值之后,总获得的权值甚至 <0 ,那就考虑将前面的那一段不考虑,sum赋值为0
ans = max(ans,sum);
}
cout<< ans <<endl;
return 0;
}
/**
3 5
-50 -47 36 -30 -23
17 -19 -34 -13 -8
-42 -3 -43 34 -45
**/
E题 零件分组
题意:
给出若干个零件,每个零件都有两个属性,长度L和重量W,要对零件进行分组,每组中每个零件的长度 L 和重量 W 都能排成一个长度和重量都不下降(若i<j,则Li≤Lj,Wi ≤ Wj)的序列,求解最少的分组
长度和重量均不超过10000
给n个物品进行分组,对于同一组中
这一组里的零件可以排序成单调不减的情况下
比如:
重量 1 2 3 4
对应的长度是 123 123 456 678
思路:
首先长度和重量两个变量,同时考虑不好处理,所以就对其中的长度进行从小到大进行排序,如果长度相同的话按照重量从小到大进行排序,首先让长度满足不下降这种状态,然后其余的只考虑重量就好
情况①某一个零件的重量小于某一组的最后面零件的重量,就要放在其他满足题意的分组,或者重新建立一个分组放入
情况②一个零件的重量大于某一组最后面零件的重量,可不可以直接放入?答案是不一定
假如进行到下面这种情况:
第一组有【1,2】和【3,4】
第二组最后面是【4,7】
如果未分组的【6,7】放到第一个分组的话,后面的【7,5】就没有办法放到第二个分组(因为重量5小于第二组最后面零件的重量),这时候就要重新建立一个分组,要用到三个组
但是如果将【6,7】放到第二个分组,那么后面的【7,5】就可以顺其自然的放到第一个分组
此时答案是2,
以上两种放置的方法显然是第二种方法更优
所以对于情况②贪心的策略就是每次添加的时候,添加到某一组最后一个零件重量和要添加的这个零件的重量最接近的那一组的后面
注意:不理解可以联想一下当时讲过的活动安排问题,在这里重量越接近,后面可能容纳零件得就可能越多
在写代码的时候,可以用一个数组标记一下每一组最后面的零件的重量,如果是某一组最后面零件的重量,设为true,否则设为false
(比如重量6是某一组最后面零件的重量,那么hava[6] = true,反之标记hava[6] = false)
上面已经提到,每次添加的时候,添加到某一组最后一个零件重量和要添加的这个零件的重量最接近的那一组的后面,那么应该怎么找到这一组呢?
=> 可以这样来想,因为想要找的那个重量一定是小于等于当前的重量,所以就可以从当前重量到1进行遍历,如果说遇到前面提到hava[j] == true,那么这一组就一定是最优的,然后将这个重量标记为false,将这一组最后面零件的重量标记为刚加入的零件的重量(have[j] = false,hava[a[i].w] = true)
当然很容易想到,因为重量最大是10000,那么我在最后进行遍历的时候,如果说这个为true,那么就是某一组的最后面的零件,就是一个组,统计有多少个标记即为最终有多少个组,就是答案
代码:
(当然会有更优的方法~)
/*** keep hungry and calm PushyTao!***/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct Node {
int l;
int w;
} a[1007];
bool cmp(Node a,Node b) {
if(a.l != b.l) return a.l < b.l;
else return a.w < b.w;
}
bool have[10007];
int main() {
int n;
cin >> n;
for(int i=1; i<=n; i++) {
scanf("%d",&a[i].l);
scanf("%d",&a[i].w);
}
sort(a + 1,a + 1 + n,cmp);
for(int i=1; i<=n; i++) {///遍历n个零件
/**
长度和重量均为不超过10000的正整数
倒着向前寻找此时如果存在就是和这个零件最接近的
**/
for(int j=a[i].w; j>=1; j--) {
if(have[j]) {
have[j] = false;///因为要替换掉,所以这个重量就不再是最后面的那一个零件的重量,设置为false
break;///结束循环
}
}
have[a[i].w] = true;///此时这个零件就是某一组最后面的一个零件,标记为true
}
int ans = 0;
for(int i=1; i<=10000; i++) {
if(have[i]) ans ++;///有多少个标记就是有多少个几组
}
printf("%d\n",ans);
return 0;
}
/**
5
8 4 3 8 2 3 9 7 3 5
->2
**/