Codeforces CodeCraft-20 (Div. 2) E贪心,费用流

Codeforces CodeCraft-20 (Div. 2) E,费用流
原题

题意:

现有n个人需要成为队员或者观众,一个team有p(<7)个位置,需要k个观众。
每个人 当观众贡献ai ,当不同位置的队员分别贡献pij
问如何安排贡献最大。

思路:

这个题官方题解状态压缩DP,这里考虑一个网络流做法。
不考虑要选k个观众,显然就是一个简单的dp。
因为还要选k个观众,需要要分析这个问题的性质。这里我们想一个贪心的思路,对队员按ai进行排序。
先不考虑队员,则我们先选取topk的观众,加进来队员的选择,发现可以分为两种情况讨论:
1 选取的队员在前k个收,我们则需要在(k,n]中再选择一个当观众,考虑只有p个队队员,实际上只需要在(k,k+p]中选一个代替他来当观众。

2 选取的队员在后k个,则直接选择就好。但实际上只需要在topP中做出选择,只有一个p的时候就是最大的那个,多个p的时候涉及的选择,但是一定在topP之中。

根据上述结论,我们可以建图通过费用流帮我们选择(当然dp也可以)。需要准备的节点主要包括P[i],tmp,O(p)个点,O(p^2)条边。具体建图见代码。

代码:

#include <bits/stdc++.h>
using namespace std;
#define X first
#define Y second
#define PB push_back
#define LL long long
#define pii pair<LL,LL>
#define MEM(x,y) memset(x,y,sizeof(x))
#define bug(x) cout<<"debug "#x" is "<<x<<endl;
#define FIO ios::sync_with_stdio(false);
#define ALL(x) x.begin(),x.end()
#define LOG 20
#define lson(x) ((x)<<1)
#define rson(x) ((x)<<1|1)
const int maxn=2e5;
const LL inf = 2e9;
const int MAXN = 200000;
const int MAXM = 200000;
const int INF = 0x3f3f3f3f;
struct Edge{
    int to,next,cap,flow,cost;
} edge[MAXM];
int head[MAXN],tol,pre[MAXN],N;
LL dis[MAXN];
bool vis[MAXN];
void init(int n){
    N = n;
    tol = 0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int cap,int cost){
    edge[tol].to = v;edge[tol].cap = cap;edge[tol].cost = cost;edge[tol].flow = 0;edge[tol].next = head[u];
    head[u] = tol++;
    edge[tol].to = u;edge[tol].cap = 0;edge[tol].cost = -cost;edge[tol].flow = 0;edge[tol].next = head[v];
    head[v] = tol++;
}
bool spfa(int s,int t){
    queue<int>q;
    for(int i = 0; i < N; i++){
        dis[i] = INF;
        vis[i] = false;
        pre[i] = -1;
    }
    dis[s] = 0;
    vis[s] = true;
    q.push(s);
    while(!q.empty()){
        int u = q.front();
        q.pop();
        vis[u] = false;
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(edge[i].cap > edge[i].flow &&dis[v] > dis[u] + edge[i].cost ){
                dis[v] = dis[u] + edge[i].cost;
                pre[v] = i;
                if(!vis[v]){
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    //return dis[t]<=0;
    if(pre[t] == -1)return false;
    else return true;
}

int minCostMaxflow(int s,int t,LL &cost){
    int flow = 0;
    cost = 0;
    while(spfa(s,t)){
        LL Min = INF;
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]){
            if(Min > edge[i].cap - edge[i].flow)
                Min = edge[i].cap - edge[i].flow;
        }
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]){
            edge[i].flow += Min;
            edge[i^1].flow -= Min;
            cost += edge[i].cost * Min;
        }
        flow += Min;
    }
    return flow;
}

pair<int,vector<int>> a[maxn];
int main(){
    FIO;
    int n,p,k,x;
    cin>>n>>p>>k;
    set<int> e;
    int cnt = n;
    int P[10];
    for(int i=0;i<p;i++) P[i] = cnt++;
    int E = cnt++, S = cnt++, tmp = cnt++;
    init(cnt);
    for(int i=0;i<n;i++)cin>>a[i].X;
    for(int i=0;i<n;i++)for(int j=0;j<p;j++) cin>>x,a[i].Y.PB(x);
    sort(a,a+n,greater<pair<int,vector<int>>>());
    for(int i=0;i<p;i++){
        addedge(S,P[i],1,0);
        vector<pii> v;
        for(int j=0;j<k;j++)v.PB({-a[j].Y[i]+a[j].X,j});
        sort(ALL(v));
        for(int j=0;j<p&&j<v.size();j++) addedge(P[i],v[j].Y,1,v[j].X),addedge(v[j].Y,tmp,1,0);
        v.clear();
        for(int j=k;j<n;j++)v.PB({-a[j].Y[i],j});
        sort(ALL(v));
        for(int j=0;j<v.size()&&j<p;j++) addedge(P[i],v[j].Y,1,v[j].X),e.insert(v[j].Y);
    }
    for(int j=k;j<k+p;j++) addedge(tmp,j,1,-a[j].X),e.insert(j);
    for(int i:e) addedge(i,E,1,0);
    LL ans;
    minCostMaxflow(S,E,ans);
    ans = -ans;
    for(int i=0;i<k;i++) ans+=a[i].X;
    cout<<ans<<endl;
	return 0;
}

posted @ 2020-03-07 17:50  zhangxianlong  阅读(128)  评论(0编辑  收藏  举报