最大流 ---- 最大不相交路径数 ---- P2766 最长不下降子序列问题(网络流24题)

题目链接


最多不相交路径

这种问题变化比较多,但都能表示成以下形式:

已知一些路径,每个节点只能属于一条路径,求能选择多少条路径使它们不相交.
主要的方法是拆点,将一个点拆成两个,然后连边,容量表示该点最多经过次数


在这里插入图片描述


解题思路:

  • 对于第一问:我们直接暴力 d p [ i ] 表 示 以 第 i 个 数 结 尾 的 最 长 不 下 降 子 序 列 的 个 数 就 可 以 求 出 s dp[i]表示以第i个数结尾的最长不下降子序列的个数就可以求出s dp[i]is
  • 对于第二问:目的是求出 s s s这种路径有多少条,那么就是典型的最大路径不相交数,因为每个点只能在一个序列里面,那么我们就可以先拆点!!
  • x − > [ x , x + n ] ( n 是 数 的 个 数 ) x 是 和 源 点 相 连 , x + n 是 和 汇 点 相 连 x->[x,x+n](n是数的个数)x是和源点相连,x+n是和汇点相连 x>[x,x+n](n)xx+n,我们为了求出有多少条那么我们就要保证连向汇点的一定是 d p [ i ] 为 s 的 点 ! dp[i]为s的点! dp[i]s,而且图上连上汇点的点应该 d p [ i ] 为 1 的 点 dp[i]为1的点 dp[i]1
  • 如果 d p [ i ] = d p [ j ] + 1 ( j < i ) dp[i]=dp[j]+1(j<i) dp[i]=dp[j]+1(j<i)那么 从 j + n 向 i 连 边 流 量 为 1 从j+n向i连边流量为1 j+ni1
  • 然后 i 向 i + n 连 流 量 为 1 的 边 i向i+n连流量为1的边 ii+n1
  • 跑最大流的结果就是结果,对于第三问只是我们把 1 1 1 n n n的所有流量变成 I N F INF INF就好

代码:

#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 = 500010;
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 ++;
}

int dp[maxn];
int arr[maxn], num;
int maxs = -1;

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 <= n; ++ 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;
    cout << maxFlow << "\n";
    if(maxs == 1) {
       cout << maxFlow <<"\n";
       exit(0);
    }
    return maxFlow;
}

inline void slove_1() {
   for(int i = 1; i <= num; ++ i) dp[i] = 1;
   for(int i = 2; i <= num; ++ i)
      for(int j = 1; j < i; ++ j)
         if(arr[i] >= arr[j])
           dp[i] = max(dp[i],dp[j]+1);
   for(int i = 1; i <= num; ++ i) 
      maxs = max(maxs,dp[i]);
   cout << maxs << endl;
}

inline void build(int x) {
   ms(head,-1), cnt = 0;
   for(int i = 1; i <= num; ++ i) {
      int flow = 1;
      if(i == 1 || i == num) flow = x;
      if(dp[i] == 1) 
        add(s,i,flow), add(i,s,0);
      if(dp[i] == maxs)
        add(i+num,t,flow), add(t,i+num,0);
   }

   
   for(int i = 1; i <= num; ++ i) {
      int flow = 1;
      if(i == 1 || i == num) flow = x;
      add(i,i+num,flow), add(i+num,i,0);
   }
   
   for(int i = 1; i <= num; ++ i) 
      for(int j = 1; j < i; ++ j) 
         if(arr[i] >= arr[j] && dp[i] == dp[j] + 1) {
            add(j+num,i,1);
            add(i,j+num,0);         
         }

}

int main() {
   cin >> num;
   s = 0, t = num << 1|1;
   n = t;
   for(int i = 1; i <= num; ++ i) cin >> arr[i];
   slove_1();
   build(1);
   get_maxflow();
   build(INF);
   get_maxflow();
   return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值