2017.9.12 人员雇佣 失败总结

来源:互联网 发布:少女与战车 知乎 编辑:程序博客网 时间:2024/06/05 19:24

网络流好难啊,还被卡题意了(不卡题意我也不会

然后强套昨天的图,其实样子基本是对的,,但是实际是错的

这个题的模型就是:

一个点选s有贡献,

两个点同时选s有贡献,

两个点同时不选s无贡献,

两个点不同时选s有负贡献、


这是一开始建的图:


错误挺明显的,

首先,就算把s流向节点的流量设为两点的贡献,也不能区分都选入t与只有一个选入t


如样例,此图这样切,下面两条边的要求是2->3*3和1->3*3 

而如果要保证左边边的性质,左边的边只能取*2的,

由于不在一个集合要倒扣,所以表示选入不同集合的中间的边就没用了(如果流入中间点的边是10,就说明1和2默契值为5,如果不都选入s,就会造成15的损失,而它被限制在了10,两个要求矛盾,所以不合法)


%%%题解

所以可以根据s和其他点的割来考虑

设割掉s连出来的边,这个点就铁定在t了,那它的损失就是各个点到它的默契值

如果有同为t的怎么办?这时就需要把损失的一部分加回来

所以向下连大小为2*默契值的边,表示同选s,如果不同选s,就一定会割掉这条边,造成2*默契值加到答案里

同样,如果同选t,这条边一定不会被割,默契值不会加到答案里

由于1s2t和1t2s一样会造成相同的损失,所以是双向边,

花费直接连到t,因为割掉这条边相当于选了s,


相当于把贡献拆成了两部分,一个是基数,一个是相互的影响,利用同一个集合不会割相连的边的性质

基数设成1倍也是为了和后面的情况搭配,如果不在一个集合,基数就是1倍的,割了2倍的一个,就相当于多了3倍的默契值,用最大闭合子图相减正好是1倍

如果都在s集合,那左边的都没被割,是正确答案 ,相减正好是花费

如果都在t集合,那左边的都被割,右边的不可能被割,也是正确答案 相减是0


分析问题,特别是验证类的  分类讨论真的非常重要!



码:

#include<iostream>#include<cstdio>#include<cstring>#include<queue>using namespace std;#define inf 1000000007#define N 8000009queue<int>q;int tot=-1,hou[N],xia[N],zhong[N],v[N],yuan[N],dis[N],ans,n,cnt,s,t,a[2005][2005];void jian(int a,int b,int c){++tot,hou[tot]=yuan[a],yuan[a]=tot,zhong[tot]=b,v[tot]=c;}void jia(int a,int b,int c,int d){jian(a,b,c);jian(b,a,d);}bool bfs(int s,int t){int i;for(i=1;i<=t;i++){dis[i]=1000000009;xia[i]=yuan[i];}q.push(s);dis[s]=0;while(!q.empty()){int st=q.front();q.pop();for(i=xia[st];i!=-1;i=hou[i]){int nd=zhong[i];     if(v[i]>0&&dis[nd]>inf) { dis[nd]=dis[st]+1; q.push(nd);  }}}return dis[t]<inf;}int dfs(int o,int t,int limit){if(!limit||o==t)return limit;int flow=0,i,f;for(i=xia[o];i!=-1;i=hou[i]){xia[o]=i;int nd=zhong[i];if(dis[nd]==dis[o]+1&&(f=dfs(nd,t,min(limit,v[i])))){flow+=f;limit-=f;v[i]-=f;v[i^1]+=f;if(!limit)break;}}return flow;}int dinic(int s,int t){int ans=0;while(bfs(s,t)){ans+=dfs(s,t,inf);//cout<<ans<<"   ";}return ans;}int main(){int i,x,j;memset(yuan,-1,sizeof(yuan));scanf("%d",&n);s=n+2;t=n+3;for(i=1;i<=n;i++){scanf("%d",&x);jia(i,t,x,0);}for(i=1;i<=n;i++){int lin=0;for(j=1;j<=n;j++){scanf("%d",&a[i][j]);        lin+=a[i][j];}ans+=lin;jia(s,i,lin,0);     }    for(i=1;i<=n;i++)    for(j=i+1;j<=n;j++)    jia(i,j,2*a[i][j],2*a[i][j]);    printf("%d",ans-dinic(s,t));}







原创粉丝点击