HDU 3998 Sequence(最大流+最長上升子序列)

題目大意:一個序列,讓你求出最大上升子序列是多少,然後有多少個最長上升子序列(即每一個元素都不相同)

思路:用nlogn的方法求出最長上升子序列.

這邊講一下網絡流的建圖模型:

添加超級源點和超級匯點.

每一個序列長度爲1的元素與源點容量爲1;

並且長度爲x與x-1的元素有一條容量爲1的邊.表示可以屬於同一個序列.

這邊我是用vector存下每個長度的元素編號,下次直接拿出來建邊就可以了.

長度爲最大的元素與匯點有一條容量爲1的邊.

跑一邊網絡流就可以了.

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
#define MAXN 100010
#define MAXM 1000001
#define INF 0x7ffffff
int head[MAXN], dep[MAXN], gap[MAXN], cur[MAXN];
int a[MAXN];
int f[MAXN];
vector<int>ff[MAXN];
int cnt;
struct edge
{
	int v;
	int cap;
	int flow;
	int next;
}edg[MAXM];

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

void addedge(int u, int v, int w, int rw = 0)
{
	edg[cnt].v = v;
	edg[cnt].cap = w;
	edg[cnt].flow = 0;
	edg[cnt].next = head[u];
	head[u] = cnt++;
	edg[cnt].v = u;
	edg[cnt].cap = rw;
	edg[cnt].flow = 0;
	edg[cnt].next = head[v];
	head[v] = cnt++;
}

int Q[MAXN];
void BFS(int start, int end)
{
	memset(dep, -1, sizeof(dep));
	memset(gap, 0, sizeof(gap));
	gap[0] = 1;
	int front = 0, rear = 0;
	dep[end] = 0;
	Q[rear++] = end;
	while (front != rear)
	{
		int u = Q[front++];
		for (int i = head[u]; i != -1; i = edg[i].next)
		{
			int v = edg[i].v;
			if (dep[v] != -1)continue;
			Q[rear++] = v;
			dep[v] = dep[u] + 1;
			gap[dep[v]]++;
		}
	}

}

int S[MAXN];
int sap(int start, int end, int n)
{
	BFS(start, end);
	memcpy(cur, head, sizeof(head));
	int top = 0;
	int u = start;
	int ans = 0;
	while (dep[start] < n)
	{
		if (u == end)
		{
			int Min = INF;
			int inser;
			for (int i = 0; i < top; i++)
			if (Min>edg[S[i]].cap - edg[S[i]].flow)
			{
				Min = edg[S[i]].cap - edg[S[i]].flow;
				inser = i;
			}
			for (int i = 0; i < top; i++)
			{
				edg[S[i]].flow += Min;
				edg[S[i] ^ 1].flow -= Min;
			}
			ans += Min;
			top = inser;
			u = edg[S[top] ^ 1].v;
			continue;
		}
		bool flag = false;
		int v;
		for (int i = cur[u]; i != -1; i = edg[i].next)
		{
			v = edg[i].v;
			if (edg[i].cap - edg[i].flow&&dep[v] + 1 == dep[u])
			{
				flag = true;
				cur[u] = i;
				break;
			}
		}
		if (flag)
		{
			S[top++] = cur[u];
			u = v;
			continue;
		}
		int Min = n;
		for (int i = head[u]; i != -1; i = edg[i].next)
		if (edg[i].cap - edg[i].flow&&dep[edg[i].v] < Min)
		{
			Min = dep[edg[i].v];
			cur[u] = i;
		}
		gap[dep[u]]--;
		if (!gap[dep[u]])return ans;
		dep[u] = Min + 1;
		gap[dep[u]]++;
		if (u != start)u = edg[S[--top] ^ 1].v;
	}
	return ans;
}

int Scan()//讀入外掛  
{
	int res = 0, ch, flag = 0;

	if ((ch = getchar()) == '-')                //判斷正負  
		flag = 1;

	else if (ch >= '0' && ch <= '9')            //得到完整的數  
		res = ch - '0';
	while ((ch = getchar()) >= '0' && ch <= '9')
		res = res * 10 + ch - '0';

	return flag ? -res : res;
}

int find(int *a, int len, int n)//修改後的二分查找,若返回值爲x,則a[x]>=n  
{
	int left = 0, right = len, mid = (left + right) / 2;
	while (left <= right)
	{
		if (n>a[mid]) left = mid + 1;
		else if (n<a[mid]) right = mid - 1;
		else return mid;
		mid = (left + right) / 2;
	}
	return left;
}

int main()
{
	int n;
	while (~scanf("%d", &n))
	{
		a[0] =-0x7ffffff;
		int S = 0;
		int E = n + 1;
		for (int i = 1; i <= n; i++)
		{
			scanf("%d", &a[i]);
			ff[i].clear();
		}
		int cur = 1;
		f[1] = a[1];
		init();
		int cnt2 = 2;
		addedge(S, 1, 1);
		int maxf = 1;
		ff[0].push_back(0);
		ff[1].push_back(1);
		for (int i = 2; i <= n; i++)
		{
			int k = find(f, cur, a[i]);
			f[k] = a[i];
			ff[k].push_back(i);
			maxf = max(maxf, k);
			cur = max(cur, k);
			k--;
			for (int j = 0; j < ff[k].size(); j++)
			{
				addedge(ff[k][j], i, 1);
			}
		}
		for (int i = 0; i < ff[maxf].size();i++)
			addedge(ff[maxf][i], E, 1);
		printf("%d\n", maxf);
		printf("%d\n", sap(S, E, E));
	}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章