洛谷 P3872 [TJOI2010]电影迷(最小割)

首先有一道题可以作为此类问题的模型:洛谷 P1646 [国家集训队]happiness
简单地说就是每个点由S和向T连边,分别表示选/不选该点;点与点之间连边,表示两个点分别选某一状态的额外权值。

本题:
S向每个点连边,容量1000-v;每个点向T连边,容量1000;每组dXY由y向x连边,容量d。n*1000-最小割即为答案。

总结技巧:
1、权值取负可以化最大值为最小割。
2、处理负权边可以全部加一个常数转为正值,但必须保证常数在每个答案中被计算次数相同,否则不能保证答案正确性。(例如本题中每个点与S和T的连边都+1000,因为每个点的这两条边都要割且只割一条,所以这个1000一定被算了n次,这样对于所有割大小的比较相当于没有影响)

本题似乎也可以用最大权闭合子图的方法解决。最大权闭合子图:https://www.cnblogs.com/TreeDream/p/5942354.html

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int maxn=110;
const int INF=0x3f3f3f3f;
int n,m,S=0,T,ans;

int head[maxn],Ecnt;
struct edge{
    int to,nxt,w;
}G[maxn*maxn*2];

inline void addE(int u,int v,int w){
    G[Ecnt]=(edge){v,head[u],w};
    head[u]=Ecnt++;
    G[Ecnt]=(edge){u,head[v],0};
    head[v]=Ecnt++;
}

int dep[maxn];
bool bfs(){
    queue <int> Q;
    memset(dep,-1,sizeof(dep));
    Q.push(S),dep[S]=1;
    while(!Q.empty()){
        int u=Q.front(); Q.pop();
        if(u==T) return 1;
        for(int i=head[u];i!=-1;i=G[i].nxt){
            int v=G[i].to;
            if(dep[v]!=-1||G[i].w==0) continue;
            Q.push(v),dep[v]=dep[u]+1;
        }
    }
    return 0;
}

int dfs(int u,int minw){
    int flow=0;
    if(u==T) return minw;
    for(int i=head[u];i!=-1;i=G[i].nxt){
        int v=G[i].to;
        if(dep[v]!=dep[u]+1||G[i].w==0) continue;
        int tmp=dfs(v,min(G[i].w,minw));
        flow+=tmp,minw-=tmp,G[i].w-=tmp,G[i^1].w+=tmp;
        if(minw==0) break;
    }
    return flow;
}

int dinic(){
    int ret=0;
    while(bfs()) ret-=dfs(S,INF);
    ret+=1000*n;
    return ret;
}

int main(){
    memset(head,-1,sizeof(head));
   scanf("%d%d",&n,&m);
    T=n+1;
    for(int i=1;i<=n;i++){
        int v;
        scanf("%d",&v);
        addE(S,i,1000-v),addE(i,T,1000);
    }
    for(int i=1;i<=m;i++){
        int x,y,d;
        scanf("%d%d%d",&x,&y,&d);
        addE(y,x,d);
    }
    printf("%d\n",dinic());
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值