有上下界网络流 ---- P4843 清理雪道(DAG图上最小路径重复边覆盖)【模板】有源汇上下界最小流

题目链接

题目大意:

在这里插入图片描述


解题思路:

  1. 首先我们发现对于每条边至少要覆盖一次,最多覆盖无数次
  2. 那么就有点像上下界网络流了 [ 1 , I N F ] [1,INF] [1,INF]的限制关系
  3. 跑一边最小流就可以了!!

#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = N;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
   x = 0;char ch = getchar();ll f = 1;
   while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
   while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args)  {
   read(first);
   read(args...);
}
struct node {
   int to, next, len; 
}e[maxn];
int head[maxn], cnt;
int n, m, s, t;
inline void add(int from, int to, int len) {
   e[cnt] = {to,head[from],len};
   head[from] = cnt ++;
   e[cnt] = {from,head[to],0};
   head[to] = cnt ++;
}

int d[maxn],cur[maxn];
int pre[maxn], flow[maxn];

bool bfs() {
   ms(d,0);
   queue<int> q;
   q.push(s); d[s] = 1;
   while(!q.empty()) {
      int u = q.front(); q.pop();
      for(int i = head[u]; ~i; i = e[i].next) {
         int v = e[i].to;
         if(d[v] || e[i].len <= 0) continue;
         q.push(v);
         d[v] = d[u] + 1;
      }

   }      
   for(int i = 0; i <= t; ++ i) cur[i] = head[i];
   return d[t] != 0;
}

int dfs(int u, int flow) {
    if(u == t) return flow;
    for(int &i = cur[u]; ~i; i = e[i].next) {
        int v = e[i].to;
        if(d[u] + 1 != d[v] || e[i].len <= 0) continue;
        int delta = dfs(v,min(flow,e[i].len));
        if(delta <= 0) continue;
        e[i].len -= delta;
        e[i^1].len += delta;
        return delta;
    }
    return 0;
}

int get_maxflow() {
    int maxFlow = 0, delta;
    while(bfs())//bfs进行构建最短路网络
         while(delta = dfs(s,INF))
             maxFlow += delta;
    return maxFlow;
}
//......................
int Tn, Tm, Gx[maxn], in[maxn], out[maxn];
void init() {
	ms(in,0), ms(out,0), ms(head,-1);
	cnt = 0;
}
int main() {
    IOS;
    init();
    cin >> n;
    int s1 = 0, t1 = n+1, s2 = n+2, t2 = n+3;
    for(int i = 1; i <= n; ++ i) {
    	int x;
    	cin >> x;
    	for(int j = 1; j <= x; ++ j) {
    		int id;
    		cin >> id;
    		in[id] ++;
			out[i] ++; 
    		add(i,id,INF-1);
		}
		add(s1,i,INF-1); // 每个点都可以是起点和终点
		add(i,t1,INF-1);
	}
	for(int i = 0; i <= n+1; ++ i) 
	  if(in[i] > out[i]) add(s2,i,in[i]-out[i]);
	  else add(i,t2,out[i]-in[i]);
	add(t1,s1,INF);
	s = s2, t = t2;
	get_maxflow();
	int flow1 = e[cnt-1].len;
	e[cnt-1].len=0;
	e[cnt-2].len=0;
	s = t1,  t = s1;// 和最大流不同,就是这里是从原图的汇点到源点进行流量回退
	cout << flow1 - get_maxflow();
	
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值