这道题目描述真是醉了。。题意,雇佣每一个经理有一个花费,同时雇佣两个经理可以的到2*E[i,j]的利润,如果一个经理雇佣另一个经理不雇佣会造成E[i,j]的损失(注意是倒扣)。
这种题目的做法和最大闭全子图差不多是一个道理。。简单说一下吧(其实是怕自己忘记):
一般来说都是转化成最小割,割完以后和S相连的是要选取的(对应到本题就是要雇佣的),和T相连的是不选取的,然后割表示损失的部分。因此建图如下:
首先,对于要雇佣的,要和S相连,就要和T割断,每一个点都是如此,因此每一个点(表示每一个经理)向T连一条边,边权为雇佣的费用,这样和T相连的就不需要割断,因此对应不雇佣的;和T割断的就是要雇佣的;
然后,对于不雇佣的,即要和T相连,和S割断,那么它损失的即为一个经理所能创造的利润E[i,1]+E[i,2]+...+E[i,n],因此将S向每一个点连一条边,容量即刚刚那个式子的结果;
最后,还要考虑损失的部分,即题中的竞争对手。如果按照上面的方法,i,j都雇佣会造成0的损失(因为和S相连的边不是割),i,j都不雇佣会造成2*E[i,j]的损失(因为和S相连的边都是割),但是一个雇佣一个不雇佣却只会造成E[i,j]的损失,但实际上损失为E[i,j]*3,因此在i,j中连一条容量为(3-1)*E[i,j]=2*E[i,j]的双向边,这样如果i不选(不妨假设),那么久要讲j->i的这一条2*E[i,j]的边也变成割,否则i和S联通,j和T联通就矛盾了,这样就使得i,j中选择一个雇佣的损失变为了E[i,j]就可以了。
因此,总的建图如下:
1.从S到任意点i连一条容量为Σ(j=1,n)E[i,j]的边;
2.任意两点i,j间连一条容量为2*E[i,j]的双向边;
3.任意点i到T连一条边,容量为雇佣的费用;
AC代码如下(发现当前弧优化好快9000+ms直接变成6000-ms了):
#include<iostream> #include<cstdio> #include<cstring> #define ll long long #define N 2005 #define M 4000005 using namespace std; int n,tot=1,d[N],h[N],fst[N],cur[N],pnt[M],nxt[M]; ll s[N],len[M],inf=(ll)100000000*(ll)100000000; void add(int aa,int bb,ll cc){ pnt[++tot]=bb; len[tot]=cc; nxt[tot]=fst[aa]; fst[aa]=tot; } bool bfs(int sta,int gol){ memcpy(cur,fst,sizeof(fst)); memset(d,-1,sizeof(d)); d[sta]=1; int head=0,tail=1; h[1]=sta; while (head<tail){ int x=h[++head],p; for (p=fst[x]; p; p=nxt[p]) if (len[p]){ int y=pnt[p]; if (d[y]==-1){ d[y]=d[x]+1; h[++tail]=y; } } } return d[gol]!=-1; } ll dfs(int x,ll rst){ if (x==n+1 || !rst) return rst; int p; ll flow=0; for (p=cur[x]; p; p=nxt[p]) if (len[p]){ int y=pnt[p]; if (d[x]+1==d[y]){ ll tmp=dfs(y,min(rst,len[p])); if (!tmp) continue; flow+=tmp; rst-=tmp; len[p]-=tmp; len[p^1]+=tmp; if (!rst) return flow; } } cur[x]=(p)?p:fst[x]; if (!flow) d[x]=-1; return flow; } int main(){ scanf("%d",&n); int i,j; ll x,ans=0; for (i=1; i<=n; i++){ scanf("%lld",&x); add(i,n+1,x); add(n+1,i,0); } for (i=1; i<=n; i++) for (j=1; j<=n; j++){ scanf("%lld",&x); ans+=x; s[i]+=x; if (i<j){ add(i,j,x<<1); add(j,i,x<<1); } } for (i=1; i<=n; i++){ add(0,i,s[i]); add(i,0,0); } while (bfs(0,n+1)) ans-=dfs(0,inf); printf("%lld\n",ans); return 0; }
2016.1.24