POJ 3614
题目:http://poj.org/problem?id=3614
题意:c头牛晒太阳,因此要涂防晒霜,第i头牛需要 minSPF 和 maxSPF 单位强度之间的防晒霜,防晒霜有l种,第i种是SPF 单位强度的防晒,并且可以涂cover次,求最多可以满足多少头牛晒太阳。
解析:
因为题中有minspf和maxspf两个变量需要考虑,因此考虑对其中一个排序来减少需要考虑的变量,此处选择对minspf升序排序。
遍历方向有两个,一个是从小到大,一个是从大到小。
应该是从大到小遍历,然后每次取能取中尽可能大的。对于该防晒,假如不被这头牛取走而被另外的牛取走,因为它是能取的里面最大的,因此所有小于它的符合条件的防晒也都符合这头牛和另外那头牛取走的条件,此时无法产生更优的选择(更优的选择指的是不被这头牛取走,而这头牛可以去得到另外那头牛取不到的防晒,这样就会产生更优解)
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 3000;
struct node {
int l, r;
}num[maxn];
int val[maxn];
bool cmp(node a,node b) {
return a.l > b.l;
}
int main() {
int n, m; cin >> n >> m;
memset(val, 0, sizeof(val));
for (int i = 1; i <= n; i++) scanf("%d %d", &num[i].l, &num[i].r);
for (int i = 1; i <= m; i++) {
int inp1,inp2; scanf("%d %d", &inp1,&inp2);
//scanf("%d", &val[inp]);
val[inp1] += inp2;
}
sort(num + 1, num + n + 1, cmp);
int cnt = 0;
for (int i = 1; i <= n; i++) {
for (int j = num[i].r; j >= num[i].l; j--) {
if (val[j]) {
val[j]--;
cnt++;
break;
}
}
}
cout << cnt;
return 0;
}
POJ 3190
题目:http://poj.org/problem?id=3190
题意:
这里有N只 (1 <= N <= 50,000) 挑剔的奶牛! 他们如此挑剔以致于必须在[A,B ]的时间内产奶(1 <= A <= B <= 1,000,000)当然, FJ必须为他们创造一个决定挤奶时间的系统.当然,没有牛想与其他奶牛分享这一时光
帮助FJ做以下事:
- 使每只牛都有专属时间的最小牛棚数
- 每只牛在哪个牛棚
也许有很多可行解。输出一种即可,采用SPJ (翻译来自vjudge)
题解:
我看到这道题第一思路就是逐个逐个牛棚,先将每一头牛按照产奶时间A 从小到大排序,A一样再按B从小到大排,接着从第一头牛开始判断,往下遍历,遇到某头牛的A比第一头牛的B要大就更新,然后标记改牛。然后再从头开始选择第二个牛棚。为了减少时间复杂度,遍历的时候可以采用倍增的思想,但是时间复杂度还是很高。
本题可以使用优先队列,优先队列按照结束时间升序排列,对于牛的初始化是按照上面的方法一样,然后只需要遍历一次,每次取堆顶,假如堆顶结束时间小于该点开始时间,那么pop,再push新的这点,否则直接push。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int maxn = 5e4 + 9;
struct node {
int l, r, ind;
}num[maxn];
bool cmp(node a, node b) {
if (a.l < b.l) return 1;
else if (a.l == b.l) return a.r < b.r;
else return 0; //!!
}
typedef pair<int,int> P;
priority_queue<P, vector<P>, greater<P>> Q;
int flag[maxn];
int main() {
int n; cin >> n;
for (int i = 1; i <= n; i++) scanf("%d %d", &num[i].l, &num[i].r), num[i].ind = i;
sort(num + 1, num + 1 + n, cmp);
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (Q.empty()) {
cnt++;
Q.push(P(num[i].r, cnt));
flag[num[i].ind] = cnt;
continue;
}
P p = Q.top();
if (p.first < num[i].l) {
Q.push(P(num[i].r, p.second));
Q.pop();
flag[num[i].ind] = p.second;
}
else {
cnt++;
Q.push(P(num[i].r, cnt));
flag[num[i].ind] = cnt;
}
}
cout << cnt << endl;
for (int i = 1; i <= n; i++) {
printf("%d\n", flag[i]);
}
return 0;
}
POJ 1328
题目:http://poj.org/problem?id=1328
解法:
二维化一维嘛,很正常的思路,求出每个点可以检测到的雷达可以放置的范围,然后将问题转化为一维区间放置最少的点使得区间能被点覆盖,将区间按照结束排序,具体看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 1e3 + 7;
struct node {
double l, r;
}num[maxn];
inline bool cmp(node a, node b) {//按结束时间排序
return a.r < b.r;
}
int main() {
int n, d;
int cnt = 0;
while (scanf("%d %d", &n, &d), n) {
cnt++;
int ok = 1;
if (d <= 0) ok = 0;
for (int i = 1; i <= n; i++) {
int a, b;
scanf("%d %d", &a, &b);
double flag = 1.0*d*d - 1.0*b*b;
if (flag < 0) {
ok = 0;
continue;
}
flag = sqrt(flag);
num[i].l = a - flag;
num[i].r = a + flag;
}
if (!ok) {
cout << "Case " << cnt << ": " << -1 << endl;
continue;
}
sort(num + 1, num + 1 + n, cmp);
int ans = 0;
double las_x = -1000000.0;
for (int i = 1; i <= n; i++) {
if (num[i].l > las_x) {
ans++;
las_x = num[i].r;
}
}
cout << "Case " << cnt << ": " << ans << endl;
}
return 0;
}
POJ 2054 Color a Tree
http://poj.org/problem?id=2054
题意: 给一棵树染色,对某一个点染色的时候,其父节点必须被染色,一开始染根节点。
输入:
第一行两个数:节点数n,根节点r
第二行n个数,每个根节点的价值vi
第三行~最后,每行两个数:父节点,节点
染色代价=已经染了的点的数目*节点的价值。
这道题贪心我想不出来,我能想出来的是递归,就是染了根节点之后其子节点可以看成另外全新的树,但是不晓得怎么写。
贪心的做法是:
树的节点里面最大的那个点,假如可以染,一定会优先染。那么染那个点的条件是染它的父节点,也就是说染父节点+染最大的那个点这个操作是绑定的,所以我们可以将这两个点合并。合并后变成一个全新的点,该点的价值=点的总价值/点由几个点组成。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 1e3 + 7;
int n, r;
int Rank[maxn]; // Rank 记录的是染色路径,rank【i】=j --- 先i在j
struct node {
int fat, c ,crow; //fat : 父节点 c:价值 crow:这个点由几个点组成
double val,total; //val,total : 因为需要取平均值,所以这两个用于比较用途
}num[maxn];
int trace(int nod) { // 找出一个大点里面最后被染色的点
return Rank[nod] == -1 ? nod : trace(Rank[nod]);
}
int main() {
while (scanf("%d %d", &n, &r), n&&r) {
memset(Rank, -1, sizeof(Rank));
//输入
for (int i = 1; i <= n; i++) {
scanf("%d", &num[i].c);
num[i].val = num[i].c*1.0;
num[i].total = num[i].val;
num[i].crow = 1;
}
for (int i = 2; i <= n; i++) {
int fro, to; scanf("%d %d", &fro, &to);
num[to].fat = fro;
}
num[r].fat = 0;
while (1) {
double maxx = -1;
int maxx_i;
for (int i = 1; i <= n; i++) { //找出权值最大的点
if (num[i].val > maxx &&i != r) maxx_i = i, maxx = num[i].val;
}
if (maxx == -1) break; //找不到就退出
int fat = num[maxx_i].fat;
num[fat].total += num[maxx_i].total; //合并点 , 处理权值
num[fat].crow += num[maxx_i].crow;
num[fat].val = num[fat].total / num[fat].crow;
num[maxx_i].val = -1;
for (int i = 1; i <= n; i++) { //合并点,处理节点链接问题,这里采取向根部方向合并
if (num[i].fat == maxx_i) num[i].fat = fat;
}
Rank[trace(fat)] = maxx_i; //记录
}
int sum = 0, day = 1, ind = r;
while (ind!=-1) {
sum += day++ * num[ind].c;
ind = Rank[ind];
}
cout << sum << endl;
}
return 0;
}