首页 > 代码库 > 最长递增子序列问题(费用流).cpp

最长递增子序列问题(费用流).cpp

http://acm.nefu.edu.cn/JudgeOnline/problemShow.php?problem_id=487

 

费用流,每次沿着最长边增广

 

//http://www.cnblogs.com/IMGavin/
#include <iostream>
#include <stdio.h>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <vector>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#include <algorithm>
using namespace std;

typedef long long LL;
#define gets(A) fgets(A, 1e8, stdin)
const int INF = 0x3F3F3F3F, N = 20008, MOD = 1003, M = 2000000;

int len;
int dp[N];//dp[i]表示当前状态若最长子序列长度为i+1时,序列最后位置数字(尽可能小)
int LIS(int *a,int n){//n为a的长度,a为待处理序列,返回a最长子序列长度
    int i,j,len,pos;
    dp[0]=a[0];
    len=0;
    for(i=1;i<n;i++){
        pos=lower_bound(dp,dp+len+1,a[i])-dp;
        dp[pos]=a[i];
        if(pos>len){
            len++;
        }
    }
    len++;
    return len;
}
struct Node{
	int u, v, cap, cost;
	int next;
}edge[M];//有向图,u到v的容量,费用
int tot;
int head[N], pre[N], path[N], dis[N];
bool inq[N];

void init(){
	tot = 0;
	memset(head, -1, sizeof(head));
}

void add(int u, int v, int cap, int cost){
	edge[tot].u = u;
	edge[tot].v = v;
	edge[tot].cap = cap;
	edge[tot].cost = cost;
	edge[tot].next = head[u];
	head[u] = tot++;
	edge[tot].u = v;
	edge[tot].v = u;
	edge[tot].cap = 0;
	edge[tot].cost = -cost;
	edge[tot].next = head[v];
	head[v] = tot++;
}

bool SPFA(int st, int des){//计算最小费用
	memset(inq, 0, sizeof(inq));
	memset(dis, 0x3f, sizeof(dis));
	queue <int> q;
	q.push(st);
	dis[st] = 0;
	inq[st] = true;
	while(!q.empty()){
		int u = q.front();
		q.pop();
		inq[u] = false;
		for(int i = head[u]; ~i; i = edge[i].next){
			int v = edge[i].v;
			if(edge[i].cap > 0 && dis[v] > dis[u] + edge[i].cost){
				dis[v] = dis[u] + edge[i].cost;
				pre[v] = u;
				path[v] = i;
				if(!inq[v]){
					inq[v] = true;
					q.push(v);
				}
			}
		}
	}
	return dis[des] == 1 - len;
}

int EdmondsKarp(int st, int des){//最小费用最大流
	int mincost = 0, flow = 0;//最小费用与流量
	while(SPFA(st, des)){
		int f = INF;
		for(int i = des; i != st; i = pre[i]){
            if(f > edge[path[i]].cap){
                f = edge[path[i]].cap;
            }
		}
		for(int i = des; i != st; i = pre[i]){
			edge[path[i]].cap -= f;
			edge[path[i]^1].cap += f;
		}
		mincost += f * dis[des];
		flow += f;
	}
	return flow;
}

int a[N];
int in[N], out[N];
int main(){
	int n;
	while(~scanf("%d", &n)){
		memset(in, 0, sizeof(in));
		memset(out, 0, sizeof(out));
		for(int i = 1; i <= n; i++){
			scanf("%d", &a[i]);
		}
		len = LIS(a + 1, n);
		printf("%d\n", len);
		init();
		for(int i = 1;  i < n; i++){
			for(int j = i + 1; j <= n; j++){
				if(a[i] < a[j]){
					add(i, j, 1, -1);
					out[i]++;
					in[j]++;
				}
			}
		}
		for(int i = 1; i <= n; i++){
			if(!in[i]){
				add(0, i, 1, 0);
			}
			if(!out[i]){
				add(i, n + 1, 1, 0);
			}
		}
		int ans = EdmondsKarp(0, n + 1);
		printf("%d\n", ans);
		add(0, 1, INF, 0);
		add(n, n + 1, INF, 0);
		ans += EdmondsKarp(0, n + 1);
		printf("%d\n", ans);
	}
	return 0;
}

  

最长递增子序列问题(费用流).cpp