zoukankan      html  css  js  c++  java
  • 2021牛客暑期多校训练营 3C

    2021牛客暑期多校训练营 3C - Minimum grid


    题意

    给定一个(n imes n)​的网格,你需要将其中的(m)​个位置填入正整数((le k))

    使得第(i)​行整行的最大值为(b[i])​,第(j)​列整列的最大值为(c[j])​​​,并且整个网格所有数字总和最小

    求出最小总和


    思路

    每行最大值与每列最大值给出,换句话说这些数都需要在每行或每列出现一次

    单独考虑行和列,对于某个位置((i,j))(b[i] eq c[j]),则该位置对缩小答案无贡献,对应的行最大值(b[i])与列最大值(c[j])需要都出现一次

    而对于位置((i,j))(b[i]=c[j]),发现如果在该位置填入了对应最大值,则行与列的条件都满足,不需要在其余位置再出现

    而如果同行或者同列出现多个点满足(b[i]=c[j_1]=c[j_2]=cdots)(c[j]=b[i_1]=b[i_2]=cdots),对应的每个点都应当放置一个数,所以一旦取了某个位置((i,j))并且让答案减去一次最大值后,行(i)与列(j)则不能再被选择

    发现这就是模板二分图最大匹配(由于与每个点连的边权值一定相同,故所有二分图匹配算法基本都能写),每个点只能被匹配一次

    按照基本套路将行与列拆成两部分点,如果输入的可填数的点((i,j))满足(b[i]=c[j])​就让行(i)与列(j)连边,如果行(i)被匹配就减去(b[i])​​;也可直接跑最大权完美匹配(KM不确定是否能过),权值直接放在连边上便于处理

    或者像下方代码展示的一样直接跑最小费用最大流,限制每个点流量为(1),边权取反求最大费用


    代码(MCMF)

    //#include<ext/pb_ds/assoc_container.hpp>
    //#include<ext/pb_ds/hash_policy.hpp>
    #include<bits/stdc++.h>
    #define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
    #define multiCase int T;cin>>T;for(int t=1;t<=T;t++)
    #define rep(i,a,b) for(int i=(a);i<=(b);i++)
    #define repp(i,a,b) for(int i=(a);i<(b);i++)
    #define per(i,a,b) for(int i=(a);i>=(b);i--)
    #define perr(i,a,b) for(int i=(a);i>(b);i--)
    #define all(a) (a).begin(),(a).end()
    #define mst(a,b) memset(a,b,sizeof(a))
    #define pb push_back
    #define eb emplace_back
    #define fi first
    #define se second
    using namespace std;
    //using namespace __gnu_pbds;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> P;
    const int INF=0x3f3f3f3f;
    const ll LINF=0x3f3f3f3f3f3f3f3f;
    const double eps=1e-12;
    const double PI=acos(-1.0);
    const ll mod=998244353;
    const int dx[8]={0,1,0,-1,1,1,-1,-1},dy[8]={1,0,-1,0,1,-1,1,-1};
    void debug(){cerr<<'
    ';}template<typename T,typename... Args>void debug(T x,Args... args){cerr<<"[ "<<x<< " ] , ";debug(args...);}
    mt19937 mt19937random(std::chrono::system_clock::now().time_since_epoch().count());
    ll getRandom(ll l,ll r){return uniform_int_distribution<ll>(l,r)(mt19937random);}
    ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
    ll qmul(ll a,ll b){ll r=0;while(b){if(b&1)r=(r+a)%mod;b>>=1;a=(a+a)%mod;}return r;}
    ll qpow(ll a,ll n){ll r=1;while(n){if(n&1)r=(r*a)%mod;n>>=1;a=(a*a)%mod;}return r;}
    ll qpow(ll a,ll n,ll p){ll r=1;while(n){if(n&1)r=(r*a)%p;n>>=1;a=(a*a)%p;}return r;}
    ll inv(ll a){return qpow(a,mod-2);}
    ll inv(ll a,ll p){return qpow(a,p-2,p);}
    
    const int maxn=5050;
    
    struct MCMF {
        struct E {
            int from, to, cap, v;
            E() {}
            E(int f, int t, int cap, int v) : from(f), to(t), cap(cap), v(v) {}
        };
        int n, m, s, t;
        vector<E> edges;
        vector<int> G[maxn];
        bool inq[maxn];
        int dis[maxn], pre[maxn], a[maxn];
        void init(int _n, int _s, int _t) {
            n = _n; s = _s; t = _t;
            for (int i = 0; i <= n; i++)
                G[i].clear();
            edges.clear();
            m = 0;
        }
        void add(int from, int to, int cap, int cost) {
            edges.emplace_back(from, to, cap, cost);
            edges.emplace_back(to, from, 0, -cost);
            G[from].push_back(m++);
            G[to].push_back(m++);
        }
        bool spfa() {
            for (int i = 0; i <= n; i++) {
                dis[i] = 1e9;
                pre[i] = -1;
                inq[i] = false;
            }
            dis[s] = 0, a[s] = 1e9, inq[s] = true;
            queue<int> Q; Q.push(s);
            while (!Q.empty()) {
                int u = Q.front(); Q.pop();
                inq[u] = false;
                for (int& idx: G[u]) {
                    E& e = edges[idx];
                    if (e.cap && dis[e.to] > dis[u] + e.v) {
                        dis[e.to] = dis[u] + e.v;
                        pre[e.to] = idx;
                        a[e.to] = min(a[u], e.cap);
                        if (!inq[e.to]) {
                            inq[e.to] = true;
                            Q.push(e.to);
                        }
                    }
                }
            }
            return pre[t] != -1;
        }
        int solve() {
            int flow = 0, cost = 0;
            while (spfa()) {
                flow += a[t];
                cost += a[t] * dis[t];
                int u = t;
                while (u != s) {
                    edges[pre[u]].cap -= a[t];
                    edges[pre[u] ^ 1].cap += a[t];
                    u = edges[pre[u]].from;
                }
            }
            return cost;
        }
    }f;
    
    ll b[2050],c[2050];
    
    void solve()
    {
        int n,m,k;
        cin>>n>>m>>k;
        rep(i,1,n)
            cin>>b[i];
        rep(i,1,n)
            cin>>c[i];
        
        int s=2*n+1,t=2*n+2;
        f.init(2*n+2,s,t);
        
        rep(i,1,n)
        {
            f.add(s,i,1,0);
            f.add(i+n,t,1,0);
        }
        
        rep(i,1,m)
        {
            int x,y;
            cin>>x>>y;
            if(b[x]==c[y])
                f.add(x,y+n,1,-b[x]); //负权边求最大花费
        }
        
        ll ans=0;
        rep(i,1,n)
            ans+=b[i]+c[i];
        ans+=f.solve(); //减去最大花费
        
        cout<<ans<<'
    ';
    }
    int main()
    {
        closeSync;
        //multiCase
        {
            solve();
        }
        return 0;
    }
    

  • 相关阅读:
    PostgreSQL事务特性之嵌套事务
    __attribute__((format(printf, a, b)))
    N个数依次入栈,出栈顺序有多少种?
    操作系统页面置换算法(opt,lru,fifo,clock)实现
    codeforces Round #320 (Div. 2) C. A Problem about Polyline(数学) D. "Or" Game(暴力,数学)
    基于X86平台的PC机通过网络发送一个int(32位)整数的字节顺序
    c/c++多线程模拟系统资源分配(并通过银行家算法避免死锁产生)
    Windows下使用Dev-C++开发基于pthread.h的多线程程序
    斐波那契的四种求法
    红黑树的插入
  • 原文地址:https://www.cnblogs.com/stelayuri/p/15081209.html
Copyright © 2011-2022 走看看