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;
}