<span style="font-family: Arial, Helvetica, sans-serif;">选了一个点,必需选择其他点,故题意为求一个闭合图(闭合图:在一个图中,我们选取一些点构成集合,记为V,且集合中的出边(即集合中的点的向外连出的弧),所指向的终点(弧头)也在V中,则我们称V为闭合图。最大权闭合图即在所有闭合图中,集合中点的权值之和最大的V,我们称V为最大权闭合图。)</span>
,构图法:取起点s,连线去所有正权的点,所有负权的点连线到t,权值为绝对值,原图的边权值为inf,最大权=正权和-s-t最大流,最小割即为方案(最后一遍bfs求增广路后,vis[i]中可以到达的点(s集合中的,满流边过不去了)即为所取)。
#include<iostream> #include<cstdio> #include<queue> #include<vector> #include<algorithm> #include<fstream> using namespace std; int n,m;int nume;int e[50000][3];int head[1005]; const int inf=100000; int vis[1005];int level[1005]; bool bfs() { for(int i=0;i<=n+m+1;i++) vis[i]=level[i]=0; queue<int>q;q.push(0);vis[0]=1; while(!q.empty()) { int cur=q.front();q.pop(); for(int i=head[cur];i!=-1;i=e[i][1]) { int v=e[i][0]; if(!vis[v]&&e[i][2]>0) { level[v]=level[cur]+1; if(v==n+1+m)return 1; vis[v]=1; q.push(v); } } } return vis[n+1+m]; } int dfs(int u,int minf) { if(minf==0||u==n+1+m)return minf; int sumf=0,f; for(int i=head[u];i!=-1&&minf;i=e[i][1]) { int v=e[i][0]; if(level[v]==level[u]+1&&e[i][2]>0) { f=dfs(v,minf<e[i][2]?minf:e[i][2]); e[i][2]-=f;e[i^1][2]+=f; minf-=f;sumf+=f; } } return sumf; } void addedge (int f,int to,int w) { e[nume][0]=to;e[nume][1]=head[f];head[f]=nume; e[nume++][2]=w; e[nume][0]=f;e[nume][1]=head[to];head[to]=nume; e[nume++][2]=0; } int dinic() { int sum=0; while(bfs()) sum+=dfs(0,inf); return sum; } vector<int>vv; int main() { // ifstream cin("shuttle.in"); // ofstream out("shuttle.out"); cin>>m>>n; for(int i=0;i<=m+n+1;i++) head[i]=-1; nume=0; int sum=0;int x,y,w; for(int i=1;i<=m;i++) { cin>>w; addedge(0,i,w); while(cin.peek()!=' ') { cin>>y; addedge(i,y+m,inf); } sum+=w; } for(int i=m+1;i<=m+n;i++) { cin>>w; addedge(i,n+m+1,w); } int ans=sum-dinic(); for(int i=1;i<n+m+1;i++) if(vis[i])vv.push_back(i); sort(vv.begin(),vv.end()); int i; for(i=0;i<vv.size()&&vv[i]<=m;i++) { cout<<vv[i]<<" "; } cout<<endl; for(;i<vv.size();i++) { if(i==vv.size()-1)cout<<vv[i]-m<<endl; else cout<<vv[i]-m<<" "; } cout<<ans<<endl; return 0; }