P3243 [HNOI2015]菜肴制作(拓扑 + 优先队列)

这篇博客讨论了一种使用拓扑排序解决美食家小A在ATM大酒店的菜肴制作顺序问题。题目中,每个菜肴有编号和制作顺序限制,目标是在满足所有限制条件下,尽可能让小A先品尝到质量高的菜肴。通过建立反向图并利用大根堆进行拓扑排序,可以找出最优的制作顺序。当存在环时,输出 Impossible! 表示无解。博主给出了AC代码实现这一算法。
摘要由CSDN通过智能技术生成

题目描述:

知名美食家小 A 被邀请至 ATM 大酒店,为其品评菜肴。ATM 酒店为小 A 准备了 n 道菜肴,酒店按照为菜肴预估的质量从高到低给予 1 到 n 的顺序编号,预估质量最高的菜肴编号为 1。

由于菜肴之间口味搭配的问题,某些菜肴必须在另一些菜肴之前制作,具体的,一共有 m 条形如 iii 号菜肴必须先于 j 号菜肴制作的限制,我们将这样的限制简写为 (i,j)。

现在,酒店希望能求出一个最优的菜肴的制作顺序,使得小 A 能尽量先吃到质量高的菜肴:

也就是说,

  1. 在满足所有限制的前提下,1 号菜肴尽量优先制作。

  2. 在满足所有限制,1 号菜肴尽量优先制作的前提下,2 号菜肴尽量优先制作。

  3. 在满足所有限制,1 号和 2 号菜肴尽量优先的前提下,3 号菜肴尽量优先制作。

  4. 在满足所有限制,1 号和 2 号和 3 号菜肴尽量优先的前提下,4 号菜肴尽量优先制作。

  5. 以此类推。

例 1:共 4 道菜肴,两条限制 (3,1)、(4,1),那么制作顺序是 3,4,1,2。

例 2:共 5 道菜肴,两条限制 (5,2)、(4,3),那么制作顺序是 1,5,2,4,3。

例 1 里,首先考虑 1,因为有限制 (3,1) 和 (4,1),所以只有制作完 3 和 4 后才能制作 1,而根据 3,3 号又应尽量比 4 号优先,所以当前可确定前三道菜的制作顺序是 3,4,1;接下来考虑 2,确定最终的制作顺序是 3,4,1,2。

例 2 里,首先制作 1 是不违背限制的;接下来考虑 222 时有 (5,2) 的限制,所以接下来先制作 5 再制作 2;接下来考虑 3 时有 (4,3) 的限制,所以接下来先制作 4 再制作 3,从而最终的顺序是 1,5,2,4,3。现在你需要求出这个最优的菜肴制作顺序。无解输出 Impossible!(首字母大写,其余字母小写)

输入格式

第一行是一个正整数 t,表示数据组数。接下来是 ttt 组数据。对于每组数据:第一行两个用空格分开的正整数 n 和 m,分别表示菜肴数目和制作顺序限制的条目数。接下来 m 行,每行两个正整数 x,y,表示 x 号菜肴必须先于 y 号菜肴制作的限制。

输出格式

输出文件仅包含 t 行,每行 n 个整数,表示最优的菜肴制作顺序,或者 Impossible! 表示无解。

输入输出样例

输入 #1

3
5 4
5 4
5 3
4 2
3 2
3 3
1 2
2 3
3 1
5 2
5 2
4 3

输出 #1

1 5 3 4 2 

Impossible! 

1 5 2 4 3

说明/提示

【样例解释】

第二组数据同时要求菜肴 1 先于菜肴 2 制作,菜肴 2 先于菜肴 3 制作,菜肴 3 先于。

菜肴 1 制作,而这是无论如何也不可能满足的,从而导致无解。

【数据范围】

100%的数据满足 n,m≤1e5,1≤t≤3。

m 条限制中可能存在完全相同的限制。

首先建图看一下:

 

我们可以发现规律:从上向下走,如果看成BFS,就是每次选取一层(小-->大排序,就是该层选取的最优解) 【请接着看,这个只是当时的猜测】

5 3 4 2

 

 再看一个样例:

 

1 5 2 4 3

到这里,似乎可以发现是拓扑排序了!!!

那就解决80%了!

再看个样例:

 发现:如果成环就输出 :

Impossible! 

这也不难理解,因为成环无法 拓扑排序

思路:

因为题意是让我们优先找最小的

所以我们 可以存反图 + 优先队列(大根堆)

最后倒序输出就可以了

AC代码如下:
 

#include <bits/stdc++.h>
#define ll long long
#define buff                     \
    ios::sync_with_stdio(false); \
    cin.tie(0);                  \
    cout.tie(0);
#define endl "\n"
using namespace std;
const int N = 1e5 + 9;
int e[N], ne[N], h[N], idx;
int d[N];
int tuop[N], cnt;
int n, m;
priority_queue<int> q; //大根堆
void add(int a, int b)
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}
void clea()
{
    // memset(e,0,sizeof e);
    // memset(ne,0,sizeof ne);
    // memset(h,0,sizeof h);
    idx = 0;
    // while (!q.empty())
    // {
    //     q.pop();
    // }
}
bool tuopsort()
{
    while (!q.empty())
    {
        int t = q.top();
        tuop[++cnt] = t;
        q.pop();
        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            d[j]--;
            if (d[j] == 0)
                q.push(j);
        }
    }
    if (cnt == n)
        return 1;
    else
        return 0;
}
void solve()
{
    clea();
    cin >> n >> m;
    cnt = 0;
    memset(h, -1, sizeof h);
    memset(d, 0, sizeof d);
    for (int i = 1; i <= m; i++)
    {
        int a, b;
        cin >> a >> b;
        add(b, a); //存反图
        d[a]++;
    }
    for (int i = 1; i <= n; i++)
    {
        if (d[i] == 0)
            q.push(i);
    }
    if (tuopsort() == 0)
    {
        cout << "Impossible!" << endl;
    }
    else
    {
        // cout<<"********"<<endl;
        for (int i = cnt; i >= 1; i--)
        {
            cout << tuop[i] << ' ';
        }
        cout << endl;
    }
}
int main()
{
    buff int t;
    cin >> t;
    while (t--)
        solve();
}

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Joanh_Lan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值