首先get知识点讲解(活动和邀请学生的题,主要看如何建图的和结论!很重要!):
https://blog.csdn.net/Cold_Chair/article/details/52841351
结论:最大权闭合子图 = 正点和-最小割。(同时,如果题目要求不仅要是最大权,并且要求"活动"点数还最少,那代码不用变,照样满足。)
这种题型都是直接套这个结论即可。
大致思路:
就是用结论“正点和-最小割=最大权闭合子图”
建图如下:
代码:
- #include<cstdio>
- #include<cstring>
- #include<algorithm>
- #include<queue>
- #define maxn 5010
- #define maxm 50100
- #define inf 0x3f3f3f3f
- using namespace std;
- struct Edge{
- int v,c,next;
- Edge(int v,int c,int next):v(v),c(c),next(next){}
- Edge(){}
- }e[maxm*6+maxn*2];
- int p[maxn+maxm];
- int cnt,n,m,T;
- void init(){
- cnt=0;
- memset(p,-1,sizeof(p));
- }
- void insert(int u,int v,int c){
- e[cnt]=Edge(v,c,p[u]);
- p[u]=cnt++;
- }
- int d[maxn+maxm];
- bool bfs(){
- memset(d,-1,sizeof(d));
- queue<int>q;
- d[0]=0;
- q.push(0);
- while(!q.empty()){
- 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]==-1){
- //printf("%d->%d(%d)\n",u,v,d[u]+1);
- d[v]=d[u]+1;
- q.push(v);
- }
- }
- }
- return d[T]!=-1;
- }
- int dfs(int u,int flow){
- if(u==T)return flow;
- int res=0;
- for(int i=p[u];i!=-1;i=e[i].next){
- int v=e[i].v;
- if(e[i].c>0&&d[v]==d[u]+1){
- int tmp=dfs(v,min(flow,e[i].c));
- e[i].c-=tmp;
- flow-=tmp;
- e[i^1].c+=tmp;
- res+=tmp;
- if(flow==0)
- break;
- }
- }
- if(res==0)
- d[u]=-1;
- return res;
- }
- int dinic(){
- int res=0;
- while(bfs()){
- // printf("here!\n");
- res+=dfs(0,inf);
- }
- return res;
- }
- int main(){
- init();
- int p,a,b,c,sum=0;
- scanf("%d%d",&n,&m);
- T=n+m+1;//汇点
- for(int i=1;i<=n;i++){
- scanf("%d",&p);
- insert(i+m,T,p); //课程放右边
- insert(T,i+m,0);
- }
- for(int i=1;i<=m;i++){
- scanf("%d%d%d",&a,&b,&c);
- sum+=c;
- insert(i,a+m,inf);
- insert(a+m,i,0);
- insert(i,b+m,inf);
- insert(b+m,i,0);
- insert(0,i,c); //用户放左边
- insert(i,0,0);
- }
- printf("%d\n",sum-dinic());
- return 0;
- }