程序员进阶之算法练习(四十六)

正文

题目1. Ichihime and Triangle

题目链接
题目大意:
输入4个整数𝑎 , 𝑏, 𝑐, 𝑑, 并且 𝑎≤𝑏≤𝑐≤𝑑;
现在需要找三个整数𝑥, 𝑦, 𝑧,满足:
𝑎≤𝑥≤𝑏.
𝑏≤𝑦≤𝑐.
𝑐≤𝑧≤𝑑.
并且𝑥, 𝑦, 𝑧能构成一个三角形。

输入:
第一行,整数𝑡表示有t个样例数量 (1≤𝑡≤1000)
接下来每个样例一行,四个整数𝑎 , 𝑏, 𝑐, 𝑑 (1≤𝑎≤𝑏≤𝑐≤𝑑≤10^9)

输出:
每个样例一行,三个s整数𝑥, 𝑦, 𝑧;

Examples
input
4
1 3 5 7
1 5 5 7
100000 200000 300000 400000
1 1 977539810 977539810

output
3 4 5
5 5 5
182690 214748 300999
1 977539810 977539810

样例解释 3 4 5
样例解释5 5 5

题目解析:
三角形要满足两边之和大于第三边,由题目意思我们知道x≤y≤z;
所以只要满足,x+y>z即可;
所以令y=z,那么x随便取值即可。


    int t;
    cin >> t;
    while (t--) {
        int a[4];
        for (int i = 0; i < 4; ++i) {
            cin >> a[i];
        }
        cout << a[1] << " " << a[2] << " " << a[2] << endl;
    }   
   

题目2. Kana and Dragon Quest game

题目链接
题目大意:
小明在打游戏,遇到了一条恶龙;
恶龙血量为x,小明可以释放两个技能:
技能1:使龙的血量变为x/2+10,x/2为向下取整;
技能2:使龙的血量减10;
当龙的血量小于等于零时,小明会赢得胜利;

小明最多可以释放n个技能1和m个技能2,现在想知道,小明是否能否打败恶龙;

输入:
第一行,整数𝑡表示有t个样例数量 (1≤𝑡≤1000)
接下来每个样例一行,整数𝑥 , 𝑛, 𝑚 (1≤𝑥≤10^5, 0≤𝑛,𝑚≤30)

输出:
每个样例一行,如果小明可以打败恶龙,输出YES;如果无法打败,则输出NO;

Examples
input
7
100 3 4
189 3 4
64 2 3
63 2 3
30 27 7
10 9 1
69117 21 2
output
YES
NO
NO
YES
YES
YES
YES

题目解析:
题目只考虑是否能打败,而不是最优解,可以直接将技能1释放完,只要技能1不会使恶龙血量增加;
然后再全部放完技能2,看最终血量是否小于等于0即可;

思考🤔:
贪心!

    int t;
    cin >> t;
    while (t--) {
        int x, n, m;
        cin >>x >> n >> m;
        while (n && (x / 2 + 10 < x)) {
            --n;
            x = x / 2  +10;
        }
        if (x <= m * 10) {
            cout << "YES" << endl;
        }
        else {
            cout << "NO" << endl;
        }
    }   

题目3. Linova and Kingdom

题目链接
题目大意:
有n个城市,用n-1条道路相连,并且保证任意两个城市之间都可以通过道路相连;
作为女王,小明需要选出k个城市作为工业城市,剩下的n-k个城市作为旅游城市。
城市1是首都,现在要召开工业会议,每个工业城市都会派出一个使者到城市1参加会议,使者会沿着道路按照最短的路径直接到达城市1,使者的快乐值等于沿途经过的旅游城市的数量。

小明想知道,如何选择工业城市,使得所有使者的快乐值总和最大?
(城市1可以是工业城市,也可以旅游城市)
输入:
第一行,整数 𝑛 and 𝑘 (2≤𝑛≤2⋅105, 1≤𝑘<𝑛)
接下来n-1行,每行两个整数 𝑢 and 𝑣 ,表示城市u和城市v之间有一条道路 (1≤𝑢,𝑣≤𝑛)

输出:
一个整数,表示所有使者的快乐值总和的最大值;

Examples
input
7 4
1 2
1 3
1 4
3 5
3 6
4 7
output
7

样例解释

input
4 1
1 2
1 3
2 4
output
2

样例解释

题目解析:
一开始是用贪心的思路:先dfs遍历,记录每个点的深度和子节点的数量;
按照深度,从大到小排序,再按子节点的大小,从小到大排序;
然后,从深度最大的开始,填充节点;相同深度,优先填充子节点少的;
最后再遍历一遍,得到结果。 复杂度NlogN。

提交之后,喜提一个Wrong Answer。

重新思考:正确的做法是评估,每一个点的价值和代价,上面只考虑了价值,忽略了代价的问题。


vector<int> g[N];
int vis[N];

void dfsCount(int u, int dep) {
    node[u].first = dep;
    node[u].val = u;
    vis[u] = 1;
    for (int i = 0; i < g[u].size(); ++i) {
        int v = g[u][i];
        if (!vis[v]) {
            dfsCount(v, dep + 1);
            node[u].second += node[v].second + 1;
        }
    }
    node[u].cost = dep - node[u].second;
}

int main(int argc, const char * argv[]) {
    // insert code here...
    
    int n, k;
    cin >> n >> k;
    for (int i = 1; i < n; ++i) {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfsCount(1, 0);
    sort(node + 1, node + n + 1);
    
    lld sum = 0;
    for (int i = 1; i <= k; ++i) {
        sum = sum + node[i].cost;
    }
    cout << sum << endl;
    
    return 0;
}

题目4. Fedor and coupons

题目链接
题目大意:
小明去商场购物,有若干个商品排成一行。
小明有n张优惠券,每张优惠券可以覆盖id范围是(l[i], r[i])的商品,包括第l[i]、r[i]个商品;
现在小明想从中选出k张优惠券,并且让这k张优惠券共同覆盖的商品尽可能多;
输出最多的商品数,还有选中的k张购物券。

输入数据
第一行两个整数,n and k (1 ≤ k ≤ n ≤ 3·1e5)
接下来n行,每行两个整数 l[i] and r[i] ( - 1e9 ≤ l[i] ≤ r[i] ≤ 1e9)

输出数据
第一行是最多覆盖的商品数;
第二行是k个整数,表示选中的k张购物券;

Examples
input
4 2
1 100
40 70
120 130
125 180
output
31
1 2
样例解释: 共同覆盖的区间是[40, 70],总共有31种商品;
input
3 2
1 12
15 20
25 30
output
0
1 2
样例解释:没有共同覆盖的区间,任选两张即可。

题目解析:
容易知道优惠券的选择是没有先后顺序,可以对其进行排序。
先保证左区间从小到大,再考虑右区间从小到大。

对于购物券i覆盖的区间(l[i], r[i]),如果之前某个购物券(我们用j来表示),其覆盖区间的r[j] < l[i]; (j < i)
那么就有l[i] > r[j] >= l[j]; 即是购物券i的覆盖区间是在购物券j覆盖区间的右边;
并且对于购物券i+1覆盖区间,因为有l[i+1] >= l[i], 那么购物券 i+1的覆盖区间必然也是在购物券j的覆盖区间右边;

由此,我们可以维护一个优先队列:里面有若干个值,r最小的在前面,如果r相同则让大的在前面;
由于前面我们已经按照左区间从小到大排序,那么对于第i张优惠券,l[i]就是最大的left,再拿优先队列里面的top.r,就是最小右区间;
l[i]和top.r形成的区间,就是优先队列里面的所有区间的公共覆盖区间,再判断下优先队列里面的区间是否大于k。

注意题目还要求输出k个优惠券,如果求解过程去记住这个值,很容易超时;
可以只记录最长的公共覆盖区间ans,输出答案的时候通过遍历所有优惠券,如果该优惠券的覆盖区间超过输出ans,则直接输出。


#include<cstdio>
#include<cmath>
#include<stack>
#include<map>
#include<set>
#include<queue>
#include<deque>
#include<string>
#include<utility>
#include<sstream>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long lld;
const int N = 301000, M = 3010100, inf = 0x7fffffff;
const lld llinf = 0x7fffffff7fffffffll;

struct Node {
    int l, r, id;
    bool operator < (const Node &tmp) const
    {
        if (r != tmp.r) return r > tmp.r; // 优先队列默认是大顶堆,但是我们希望r小的在前面
        else return l < tmp.l; // 如果r相同,那么希望l大的在前面
    };
    Node(int first, int second, int id):l(first), r(second), id(id){};
    Node(){};
}a[N];

bool cmp(Node x, Node y) {
    if (x.l != y.l) {
        return x.l < y.l;
    }
    else {
        return x.r < y.r;
    }
}

int main(int argc, const char * argv[]) {
    // insert code here...
    int k, n;
    cin >> n >> k;
    for (int i = 0; i < n; ++i) {
        scanf("%d%d", &a[i].l, &a[i].r);
        a[i].id = i + 1;
    }
    sort(a, a + n, cmp);
    priority_queue<Node> priQueue;
    int ans = 0;
    pair<int, int> seg;
    for (int i = 0; i < n; ++i) {
        // 优先队列里面有若干个值,r最小的在前面,假设是top.r,l最大的是l[i],那么这些区间的公共区域是 l[i] 到 r
        while (!priQueue.empty()) {
            Node top = priQueue.top();
            if (top.r < a[i].l) {
                priQueue.pop();
                continue;
            }
            else {
                break;
            }
        }
        while (priQueue.size() >= k) {
            priQueue.pop();
        }
        
        if (priQueue.size() == k - 1) {
            int topR;
            if (priQueue.size() == 0) {
                topR = a[i].r;
            }
            else {
                topR = min(priQueue.top().r, a[i].r);
            }
            if (ans < topR - a[i].l + 1) {
                ans = topR - a[i].l + 1;
                seg = make_pair(a[i].l, topR);
            }
        }
        priQueue.push(a[i]);
    }
    
    if (ans == 0) {
        cout << 0 << endl;
        for (int i = 0; i < k; ++i) {
            printf("%d ", i + 1);
        }
    }
    else {
        cout << ans << endl;
        for (int i = 0; i < n && k; ++i) {
            if (a[i].l <= seg.first && a[i].r >= seg.second) {
                printf("%d ", a[i].id);
                --k;
            }
        }
        cout << endl;
    }
    
    return 0;
}

总结

题目1是简单思维,三角形的基本性质;
题目2是典型的贪心题目,代码非常简单;
题目3是贪心类似的思路,可以用代价和价值来简化思考;
题目4要先对数据做预处理,是典型的区间覆盖问题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355