Codeforces 1487E - Cheap Dinner (数据结构、排序)

Educational Codeforces Round 104 (Rated for Div. 2) E. Cheap Dinner


题意

给定 n 1 n_1 n1第一道菜的价格 a i a_i ai n 2 n_2 n2第二道菜的价格 b i b_i bi n 3 n_3 n3饮料的价格 c i c_i ci n 4 n_4 n4甜点的价格 d i d_i di

m 1 m_1 m1种组合 ( x i , y i ) (x_i,y_i) (xi,yi),描述编号为 x i x_i xi第一道菜与编号为 y i y_i yi第二道菜不能搭配

m 2 m_2 m2种组合 ( x i , y i ) (x_i,y_i) (xi,yi),描述编号为 x i x_i xi第二道菜与编号为 y i y_i yi饮料不能搭配

m 3 m_3 m3种组合 ( x i , y i ) (x_i,y_i) (xi,yi),描述编号为 x i x_i xi饮料与编号为 y i y_i yi甜点不能搭配

问是否能够每种种类的食物都分别挑选一种,它们可以互相搭配且总花费最小


限制

1 ≤ n 1 , n 2 , n 3 , n 4 ≤ 150000 1\le n_1,n_2,n_3,n_4\le 150000 1n1,n2,n3,n4150000

1 ≤ a i , b i , c i , d i ≤ 1 0 8 1\le a_i,b_i,c_i,d_i\le 10^8 1ai,bi,ci,di108

1 ≤ m 1 , m 2 , m 3 ≤ 200000 1\le m_1,m_2,m_3\le 200000 1m1,m2,m3200000




思路

很容易想到是费用流的模板题,但费用流会出现 n 2 n^2 n2条边,这题显然不可行

由于每种限制仅针对两种种类的食物,所以可以想到拆成三部分的二分图来做(当然不是二分图匹配)


我们可以先处理第二道菜

原本 b i b_i bi表示的是“编号为 i i i第二道菜的价格”

我们可以将 { a } \{a\} { a}引入,从而让 b i b_i bi表示“编号为 i i i第二道菜与某一种第一道菜搭配所能得到的最小花费”

b i = b i + m i n { a j } b_i=b_i+min\{a_j\} bi=bi+min{ aj},其中 a j a_j aj b i b_i bi能够搭配

处理好 b i b_i bi后,接下来处理饮料时

原本 c i c_i ci表示的是“编号为 i i i饮料的价格”

引入 { b } \{b\} { b},从而让 c i c_i ci表示“编号为 i i i饮料与其所能搭配的 b i b_i bi相加后,能得到的最小花费”

c i = c i + m i n { b j } c_i=c_i+min\{b_j\} ci=ci+min{ bj},其中 b j b_j bj c i c_i ci能够搭配

d i d_i di同理,最终表示“编号为 i i i甜点与其所能搭配的 c i c_i ci相加后,能得到的最小花费”

显而易见,答案就是 d i d_i di中的最小值


以将 { a } \{a\} { a}引入 b i b_i bi为例

如果我们想求出 b i = b i + m i n { a j } b_i=b_i+min\{a_j\} bi=bi+min{ aj},其中 a j a_j aj b i b_i bi能够搭配

最简便的方法就是枚举所有 a j a_j aj,但这样的时间复杂度就会来到 O ( n 2 ) O(n^2) O(n2),显然不可行

我们可以将数据存入结构体,记录原本的编号以及价格,将 { a } \{a\} { a}进行排序

我们可以将所有不合法的搭配 ( x , y ) (x,y) (x,y)存进set容器中,由于是对每个 b i b_i bi找出最小的 a j a_j aj,所以往编号为 y y y的set容器内插入值 x x x,表示 { b } \{b\} { b}中的 y y y不能与 { a } \{a\} { a}中的 x x x进行搭配

然后我们按顺序遍历排序后的 { a } \{a\} { a},每次在编号为 i i i的set容器中二分查找是否存在一个值为 j j j的元素

如果不存在,说明此时的 b i b_i bi a j a_j aj能够进行搭配,由因为保证了 a j a_j aj是目前的最小值,所以直接让 b i = b i + a j b_i=b_i+a_j bi=bi+aj即可,其后结束此次遍历

这种方法的时间复杂度为 O ( n l o g m ) O(nlogm) O(nlogm)

{ b } \{b\} { b}引入 c i c_i ci,将 { c } \{c\} { c}引入 d i d_i di同理,详见代码注释




代码

(560ms/4000ms)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=150050;

struct node
{
    
    
    int id,val;
    bool operator < (const node& a) const
    {
    
    
        return val<a.val;
    }
}a[maxn],b[maxn],c[maxn],d[maxn];

int n1,n2,n3,n4;

set<int> v1[maxn],v2[maxn],v3[maxn];

void input()
{
    
    
    cin>>n1>>n2>>n3>>n4;
    rep(i,1,n1)
        cin>>a[i].val,a[i].id=i;
    rep(i,1,n2)
        cin>>b[i].val,b[i].id=i;
    rep(i,1,n3)
        cin>>c[i].val,c[i].id=i;
    rep(i,1,n4)
        cin>>d[i].val,d[i].id=i;
    
    int m1,m2,m3;
    cin>>m1;
    while(m1--)
    {
    
    
        int a,b;
        cin>>a>>b;
        v1[b].insert(a); //往编号为b的容器内插入a
    }
    cin>>m2;
    while(m2--)
    {
    
    
        int a,b;
        cin>>a>>b;
        v2[b].insert(a);
    }
    cin>>m3;
    while(m3--)
    {
    
    
        int a,b;
        cin>>a>>b;
        v3[b].insert(a);
    }
}

void solve()
{
    
    
    input();
    
    sort(a+1,a+1+n1); //对{a}进行排序
    
    rep(i,1,n2) //遍历所有bi
    {
    
    
        bool flag=false;
        rep(j,1,n1) //对于每一个bi,遍历排完序后的{a}
        {
    
    
            if(v1[i].find(a[j].id)==v1[i].end()) //如果aj与bi能够搭配
            {
    
    
                b[i].val=min(a[j].val+b[i].val,INF); //直接相加即可,注意与无穷大取小,方便数据处理
                flag=true;
                break; //找到之后就可以直接跳出此次遍历
            }
        }
        if(!flag)
            b[i].val=INF; //如果不存在搭配,将其标记为无穷大
    }
    
    sort(b+1,b+1+n2); //下同
    
    rep(i,1,n3)
    {
    
    
        bool flag=false;
        rep(j,1,n2)
        {
    
    
            if(v2[i].find(b[j].id)==v2[i].end())
            {
    
    
                c[i].val=min(b[j].val+c[i].val,INF);
                flag=true;
                break;
            }
        }
        if(!flag)
            c[i].val=INF;
    }
    
    sort(c+1,c+1+n3);
    
    rep(i,1,n4)
    {
    
    
        bool flag=false;
        rep(j,1,n3)
        {
    
    
            if(v3[i].find(c[j].id)==v3[i].end())
            {
    
    
                d[i].val=min(c[j].val+d[i].val,INF);
                flag=true;
                break;
            }
        }
        if(!flag)
            d[i].val=INF;
    }
    
    sort(d+1,d+1+n4);
    
    if(d[1].val==INF)
        cout<<"-1\n";
    else
        cout<<d[1].val<<'\n';
}

int main()
{
    
    
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}

https://www.cnblogs.com/stelayuri/p/14405916.html

猜你喜欢

转载自blog.csdn.net/qq_36394234/article/details/113821354