P4209 学习小组
【题意】
【分析】
这个题目也用到了平方拆边的技巧,其次这里有一个保证参与学生最多的限制
这个我们可以贪心来解决,也就是说每一个学生我们先让他选一个,剩下的只有在能带给我收益的情况下才进行增广
这样我们就得到了如下的建图方式
学习小组向T建C,3C,5C,....
S向学生连(k,0)的边 ,让学生再连向T(k-1,0)
学生再向学习小组进行建立手续费的边
【代码】
#include<bits/stdc++.h> using namespace std; typedef long long ll; #define mp make_pair #define fi firstP #define se second const int maxn=200+5; const int maxm=2e4+5; int n,m,head[maxn]; struct edge { int to,nxt,v,c; }e[maxm<<1]; int tot=1,S,T; void add(int x,int y,int z,int c) { e[++tot].to=y; e[tot].nxt=head[x]; e[tot].v=z; e[tot].c=c; head[x]=tot; e[++tot].to=x; e[tot].nxt=head[y]; e[tot].v=0; e[tot].c=-c; head[y]=tot; } const int inf=0x3f3f3f3f; int dis[maxn],vis[maxn],pre[maxn]; bool spfa() { for(int i=S;i<=T;i++) vis[i]=0,dis[i]=inf; dis[S]=0; queue <int> q; q.push(S); vis[S]=1; while(!q.empty()) { int u=q.front(); q.pop(); for(int i=head[u];i;i=e[i].nxt) { int to=e[i].to; if(dis[to]>dis[u]+e[i].c && e[i].v>0) { dis[to]=dis[u]+e[i].c; pre[to]=i; if(!vis[to]) { q.push(to); vis[to]=1; } } } vis[u]=0; } return (dis[T]!=inf); } int mcmf() { int res=0,cost=0; while(spfa()) { int flow=inf; for(int i=T;i!=S;i=e[pre[i]^1].to) flow=min(flow,e[pre[i]].v); for(int i=T;i!=S;i=e[pre[i]^1].to) { e[pre[i]].v-=flow; e[pre[i]^1].v+=flow; cost+=e[pre[i]].c*flow; } } return cost; } int k,c[105],f[105],cnt[105]; int main() { // freopen("a.in","r",stdin); // freopen("a.out","w",stdout); scanf("%d%d%d",&n,&m,&k); S=0; T=n+m+1; for(int i=1;i<=m;i++) scanf("%d",&c[i]); for(int i=1;i<=m;i++) scanf("%d",&f[i]); for(int i=1;i<=n;i++) { int op; add(S,i,k,0); add(i,T,k-1,0); for(int j=1;j<=m;j++) { scanf("%1d",&op); if(op) { cnt[j]++; add(i,n+j,1,-f[j]); } } } for(int i=1;i<=m;i++) for(int j=1;j<=cnt[i];j++) add(i+n,T,1,c[i]*(j*2-1)); printf("%d",mcmf()); return 0; }