这道题肝了好久。。。我感觉我一道题就没肝过这么久。。。气死了。。。。
思路:
就是用dicnic算法求最大流的问题,关键在于如何建图:
如图(图中边权值都为1),尤其是箭头的指向、源点和汇点的设计!
①箭头的指向:由员工指向外人,员工之间由不加班的指向加班的(这是为什么呢?我是用“抱过来”思想考虑的。因为你看看这个图嘛,其实是想把外人都“抱到”员工的座位上来,那么这样走过一条增广路就能解决一个外人的座位问题,对吧!那么因为加班的员工也可以去做身为他朋友的不加班员工的座位,那同理,是不是相当于不加班员工把加班员工“抱过来”坐着的意思呢?所以箭头就是“抱过来”的意思!),加班员工之间可以互相报所以设置双向边(即反相边权值为1),其他都设置单向边(即反向边权值为0)。
②源点出来指向的是不加班的人。这需要理解题意:加班的人有自己的位子要做,那些外人肯定是要坐不加班的人的位子。那么如果根据“抱过来”坐着的思想,肯定要先从一个空位子开始“抱过来”啊对吗??!!
③所有外人指向汇点。这是显然的,因为我需要所有外人都找到位子,也就是都成功地被“抱到”空位子上,既然每个边流量都为1,看看最大流就知道最多能坐多少个外人了,只要最大流==外人数即可!
思路真的很难想我觉得,不过用“抱过来”思想一下就简单明了了。
代码:
#include<iostream> #include<bits/stdc++.h> using namespace std; const int maxn=1e3+5; const int maxm=1e6; struct edge { int v,c,next; }e[maxn]; int p[maxm]; int cnt=-1; int isstaff[maxn]; int isinjob[maxn]; void init() { memset(isstaff,0,sizeof(isstaff)); memset(isinjob,0,sizeof(isinjob)); memset(p,-1,sizeof(p)); cnt=-1; } void insert1(int u,int v,int c) { e[++cnt].v=v; e[cnt].c=c; e[cnt].next=p[u]; p[u]=cnt; } int d[maxn]; int n; //一共有多少人 bool bfs(int s) { memset(d,0,sizeof(d)); d[s]=1; queue<int> q; q.push(s); while(q.empty()==false) { int u=q.front(); q.pop(); for(int i=p[u];i!=-1;i=e[i].next) { int v=e[i].v; if(e[i].c>0 && d[v]==0) { d[v]=d[u]+1; q.push(v); } } } return (d[n+1]!=0); } int dfs(int s,int flow) //flow表示在该增广路上,s点之前所更新出来的最大流量 { if(s==n+1) return flow; int res=0; for(int i=p[s];i!=-1;i=e[i].next) { int v=e[i].v; if(d[v]==d[s]+1 && e[i].c>0) { int temp=dfs(v,min(flow,e[i].c)); flow-=temp; res+=temp; e[i].c-=temp; e[i^1].c+=temp; if(flow==0) break; } } if(res==0) d[s]=-1; return res; } int main() { int T; cin>>T; while(T--) { init(); scanf("%d",&n); int kaihui=0; //记录开会人数 for(int i=1;i<=n;i++) { scanf("%d",&isstaff[i]); //1表示是员工 if(isstaff[i]==0) //构图右边 { insert1(i,n+1,1); insert1(n+1,i,0); kaihui++; } } for(int i=1;i<=n;i++) { scanf("%d",&isinjob[i]); //0表示加班 if(isstaff[i]==1 && isinjob[i]==1) //构图左边 { insert1(0,i,1); insert1(i,0,0); } } int relationship; for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { scanf("%d",&relationship); if(i>=j) continue; if(relationship==0) continue; if(isstaff[i]==1 && isinjob[i]==0) //加班的人 { if(isstaff[j]==1 && isinjob[j]==0) //遇到加班的 { insert1(i,j,1); insert1(j,i,1); //这里不用连反向边了,本来就有了。 } else if(isstaff[j]==1 && isinjob[j]==1) //遇到不加班的 { insert1(j,i,1); insert1(i,j,0); } else if(isstaff[j]==0) //遇到外人 { insert1(i,j,1); insert1(j,i,0); } } else if(isstaff[i]==1 && isinjob[i]==1) //不加班的人 { if(isstaff[j]==1 && isinjob[j]==0) //遇到加班的 { insert1(i,j,1); insert1(j,i,0); } else if(isstaff[j]==0) //遇到外人 { insert1(i,j,1); insert1(j,i,0); } } else if(isstaff[i]==0)//外人 { if(isstaff[j]==1) { insert1(j,i,1); insert1(i,j,0); } } } } int res=0; while(bfs(0)) { res+=dfs(0,0x3f3f3f3f); } if(res!=kaihui) cout<<"T_T"<<endl; else cout<<"^_^"<<endl; } return 0; }
我在手写代码中出现了一些“致命”问题,这里强调一下:
①对于这种多组数据,链式前向星的初始化不仅要初始化p数组,还要初始化cnt啊!!!md!!
②bfs更新层次每次都需要重新初始化记录层次的d数组。
③dfs中别忘了一开始就要写递归的临界条件。。。(无语)