Educational Codeforces Round 104 (Rated for Div. 2) E. Cheap Dinner(线段树优化dp)

题意:

n 1 n_1 n1种主食, n 2 n_2 n2种副食, n 3 n_3 n3种饮料, n 4 n_4 n4种甜点,有 m 1 , m 2 , m 3 m_1,m_2,m_3 m1,m2,m3对关系, m 1 m_1 m1的每对关系代表一些主食与副食不能搭配, m 2 m_2 m2代表一些副食与饮料不能搭配, m 3 m_3 m3代表一些饮料和甜点不能搭配,每个食物都有价格,问怎么选才能使得价格最少,并且这 4 4 4类都要有,输出最少的花费。

题解:

一开始想到了费用流,但是看到数据太大,就果断放弃了,后来想到用线段树维护,但是算错复杂度了,以为是个假算法,后来仔细想想,其实是可以的。

d p [ i ] [ j ] dp[i][j] dp[i][j]表示选前 i i i 类食物,第 i i i 类食物选第 j j j 个的最小花费,数据小一点,其实就很容易想到怎么转移了:

d p [ i ] [ j ] = min ⁡ k = 1 n [ i − 1 ] d p [ i − 1 ] [ k ] + c o s t [ i ] [ j ] dp[i][j]=\min\limits_{k=1}^{n[i-1]} dp[i-1][k]+cost[i][j] dp[i][j]=k=1minn[i1]dp[i1][k]+cost[i][j]

那数据大一点就是用线段树维护一下区间最小值即可,因为存在不能搭配的食物,只需对不能搭配的食物排序,然后再对一段段的区间求值就行。我一开始以为这里的复杂度是 O ( n m l o g n ) O(nmlogn) O(nmlogn),其实是 O ( m l o g n ) O(mlogn) O(mlogn)

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int>pii;
const int MAXN=2e5+5;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
int n[5];
int a[5][MAXN];
int dp[MAXN];
vector<int>g[MAXN];
struct Node
{
    
    
    int l,r;
    int minn;
}node[5][MAXN<<2];
void build(int id,int l,int r,int num)
{
    
    
    node[id][num].l=l;
    node[id][num].r=r;
    if(l==r)
    {
    
    
        node[id][num].minn=dp[l];
        return;
    }
    int mid=(l+r)>>1;
    build(id,l,mid,num<<1);
    build(id,mid+1,r,num<<1|1);
    node[id][num].minn=min(node[id][num<<1].minn,node[id][num<<1|1].minn);
}
int query(int id,int l,int r,int num)
{
    
    
    if(node[id][num].l>=l&&node[id][num].r<=r)
    {
    
    
        return node[id][num].minn;
    }
    int mid=(node[id][num].l+node[id][num].r)>>1;
    int ans=1e9;
    if(l<=mid)
    {
    
    
        ans=min(ans,query(id,l,r,num<<1));
    }
    if(r>mid)
    {
    
    
        ans=min(ans,query(id,l,r,num<<1|1));
    }
    return ans;
}
void updata(int id,int pos,int val,int num)
{
    
    
    if(node[id][num].l==node[id][num].r)
    {
    
    
        node[id][num].minn=val;
        return ;
    }
    int mid=(node[id][num].l+node[id][num].r)>>1;
    if(pos<=mid)
    {
    
    
        updata(id,pos,val,num<<1);
    }
    else updata(id,pos,val,num<<1|1);
    node[id][num].minn=min(node[id][num<<1].minn,node[id][num<<1|1].minn);
}
int main()
{
    
    
    for(int i=1;i<=4;i++)
    {
    
    
        scanf("%d",&n[i]);
    }
    for(int i=1;i<=4;i++)
    {
    
    
        for(int j=1;j<=n[i];j++)
        {
    
    
            scanf("%d",&a[i][j]);
            if(i==1) dp[j]=a[i][j];
            else dp[j]=1e9;
        }
        build(i,1,n[i],1);
    }
    for(int i=2;i<=4;i++)
    {
    
    
        for(int j=0;j<=n[i];j++) g[j].clear();
        int m;scanf("%d",&m);
        for(int j=1;j<=m;j++)
        {
    
    
            int x,y;scanf("%d%d",&x,&y);
            g[y].push_back(x);
        }
        for(int j=1;j<=n[i];j++)
        {
    
    
            if(g[j].size()==0)
            {
    
    
                int minn=query(i-1,1,n[i-1],1);
                if(minn==1e9) continue;
                updata(i,j,minn+a[i][j],1);
            }
            else
            {
    
    
                sort(g[j].begin(),g[j].end());
                g[j].push_back(n[i-1]+1);
                int pre=1;
                int minn=1e9;
                for(int k=0;k<g[j].size();k++)
                {
    
    
                    if(g[j][k]-1>=pre) minn=min(minn,query(i-1,pre,g[j][k]-1,1));
                    pre=g[j][k]+1;
                }
                if(minn!=1e9) updata(i,j,minn+a[i][j],1);
            }
        }
    }
    int ans=query(4,1,n[4],1);
    if(ans==1e9) printf("-1\n");
    else printf("%d\n",ans);
}

猜你喜欢

转载自blog.csdn.net/weixin_45755679/article/details/114006064