最长上升子序列是一个经典问题,可以用O(n^2)的dp解决。给出一个串,求出最长上升子序列的长度为多少?假设长度为s,现在问题是,有多少个长度为s的上升子序列,满足每个子序列所包含的元素均不相同(即一个数只能选一次)。
建模:
第一问直接用dp求解,dp[i]表示以i为结尾的最长递增子序列的长度,最后取dp[1~n]的最大值,即为s。
第二问可以利用上一问求出来的dp数组,用网络流求解。
1. 拆点,将每个点拆成两点,容量为1,保证每个点只取一次。
2. 增加源点s和汇点t,s和dp[i]=1的点相连,dp[i]=s的点和t相连,容量均为INF。
3. 对于两点i和j(j<i),如果满足条件:a[j]<a[i]&&dp[j]+1=dp[i],添加有向边(j,i),容量为INF。
这样,保证从s到t的每一条路都是一条最长递增子序列,最大流即为答案。
例:HDU3998
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int INF = 0x7fffffff;
const int maxv = 2000;
const int maxe = maxv*maxv*2;
int n,s;
int a[maxv];
int dp[maxv];
int source,sink;
//
struct Edge
{
int v;
int next;
int flow;
};
Edge e[maxe];
int head[maxv],edgeNum;
int now[maxv],d[maxv],vh[maxv],pre[maxv],preh[maxv];
void addEdge(int a,int b,int c)
{
e[edgeNum].v = b;
e[edgeNum].flow = c;
e[edgeNum].next = head[a];
head[a] = edgeNum++;
e[edgeNum].v = a;
e[edgeNum].flow = 0;
e[edgeNum].next = head[b];
head[b] = edgeNum++;
}
void Init()
{
edgeNum = 0;
memset(head,-1,sizeof(head));
memset(d,0,sizeof(d));
}
int sap(int s,int t,int n) //源点,汇点,结点总数
{
int i,x,y;
int f,ans = 0;
for(i = 0; i < n; i++)
now[i] = head[i];
vh[0] = n;
x = s;
while(d[s] < n)
{
for(i = now[x]; i != -1; i = e[i].next)
if(e[i].flow > 0 && d[y=e[i].v] + 1 == d[x])
break;
if(i != -1)
{
now[x] = preh[y] = i;
pre[y] = x;
if((x=y) == t)
{
for(f = INF,i=t; i != s; i = pre[i])
if(e[preh[i]].flow < f)
f = e[preh[i]].flow;
ans += f;
do
{
e[preh[x]].flow -= f;
e[preh[x]^1].flow += f;
x = pre[x];
}while(x!=s);
}
}
else
{
if(!--vh[d[x]])
break;
d[x] = n;
for(i=now[x]=head[x]; i != -1; i = e[i].next)
{
if(e[i].flow > 0 && d[x] > d[e[i].v] + 1)
{
now[x] = i;
d[x] = d[e[i].v] + 1;
}
}
++vh[d[x]];
if(x != s)
x = pre[x];
}
}
return ans;
}
//
void build()
{
int i,j;
Init();
source = 0;
sink = 2*n+1;
for(i = 1; i <= n; i++)
addEdge(i,i+n,1);
for(i = 1; i <= n; i++)
{
if(dp[i]==1)
addEdge(source,i,INF);
if(dp[i]==s)
addEdge(i+n,sink,INF);
}
for(i = 1; i <= n; i++)
{
for(j = 1; j < i; j++)
{
if(a[j] < a[i] && dp[j]+1 == dp[i])
addEdge(j+n,i,INF);
}
}
}
int main()
{
int i,j;
while(scanf("%d",&n)!=EOF)
{
s = -1;
for(i = 1; i <= n; i++)
scanf("%d",&a[i]);
for(i = 1; i <= n; i++)
dp[i] = 1;
for(i = 1; i <= n; i++)
{
for(j = 1; j < i; j++)
{
if(a[j] < a[i] && dp[j] + 1 > dp[i])
dp[i] = dp[j] + 1;
}
}
for(i = 1; i <= n; i++)
{
if(s < dp[i])
s = dp[i];
}
printf("%d\n",s);
build();
printf("%d\n",sap(source,sink,sink+1));
}
return 0;
}
分享到:
相关推荐
6 最长递增子序列问题 最多不相交路径 网络最大流 7 试题库问题 二分图多重匹配 网络最大流 8 机器人路径规划问题 (未解决) 最小费用最大流 9 方格取数问题 二分图点权最大独立集 网络最小割 10 餐巾计划问题 线性...
再给定两个不同的顶点\(s, t \in V\)以及一个长度函数\(l: E \rightarrow \mathbb{R}^+\),任务是找到一对顶点不相交的路径\(AP\)(活跃路径)和\(BP\)(备份路径),使得较短路径的长度\(l(AP)\)最小化,其中\(l(AP...
因此,G中的一个独立集S={i1, i2, ..., it}对应于G*中的一组不相交路径Pi1, Pi2, ..., Pit。 这个归约过程可以在多项式时间内完成,因为它涉及到简单的图操作和路径构造。如果能找到k条不相交的路径,那么原始图G就...
- 选取一条边将原树分成两棵不相交的树。 - 分别对这两棵树递归处理。 - 最坏情况下,递归次数可能达到O(N)。 ### 例一:树中点对统计 给定一棵有N个节点的带权树,定义节点u和v之间的距离`dist(u, v)`为从u到v...
基于边的分治方法则是选取树中的一条边,将树分割成两棵不相交的子树,然后递归处理这两部分。这种方法在最坏情况下可能会导致O(N)次的递归调用。 ### 二、树的路径剖分算法 路径剖分算法主要用于高效地处理树的...
不相交多径算法始于主路径的建立,然后逐级寻找不与主路径重叠的次优路径。交织多径算法则是为主路径上的每个节点创建一条优化的备份路径,形成交织路径集合。当主路径失效时,可以迅速切换到交织路径中的一条,保证...
8. 两条直线相交有1个交点,三条直线两两相交最多有3个交点。 9. 图形题中考察了线段的长度比较。 测试卷的其他部分包括了正确的概念辨别,如火车轨道的平行性,字母H中的平行与垂直线段,平行线的定义,三角板的...
Menger定理的两个推论分别讨论了在无向图中找到不相交路径的边截集和点截集版本。 在有向图中,寻找弧连通度通常涉及使用网络流算法,例如Edmonds-Karp算法。这是因为网络流算法可以用来寻找两个指定顶点间最大流量...
1. 平面内直线的交点公式表明,n条直线相交最多会产生个交点。例如,平面内的四条直线可能有0至6个交点,具体取决于它们的位置关系。 2. 直线相交可以将平面分割成多个部分,n条直线最多可分区块,可以通过递推公式1...
1. 在同一平面内,不相交的两条直线是平行的,答案为B。 2. 通过直线外一点只能画一条与已知直线平行的线,答案为A。 3. 通过一点可以画无数条直线,通过两点可以画一条直线,答案分别是C和A。 4. 同一平面内的两条...
3. **路径**:由一条或多条直线构成,但在连连看游戏中,最多允许不超过三条直线来连接两个基点。 接下来,我们来看连连看的具体设计思路: 1. **选择基点**:当玩家点击两个相同的图片时,确定它们的基点P1和P2。...
此外,最小路径覆盖问题是在有向无环图中寻找最少数目的不相交简单路径以覆盖所有节点,可以通过构建二分图模型并利用最大匹配来解决。 最大独立集问题是在图G中找到最大的点子集,其中任意两点之间没有边。在二分...
二分图是由两个不相交的顶点集合构成,且每条边连接的是不同集合中的顶点。二分图匹配则是寻找在这样的图中,边的数目尽可能多但不使任何顶点被同一边连接的子图,即最大匹配问题。 最大匹配问题是寻找二分图中边数...
- **二分图**:由两个不相交的顶点集\(U\)和\(V\)以及连接这两个集合中顶点的边集\(E\)组成,其中每条边的一端位于\(U\)中,另一端位于\(V\)中。 - **匹配**:在无向图中,一个匹配是一组边,使得没有顶点属于该组的...
- 路径上的边交替地不在当前匹配中与在当前匹配中。 - 路径的起点和终点是没有被匹配的节点。 #### 四、匈牙利算法的具体步骤 1. **初始化**:将当前最大匹配设置为空。 2. **循环执行**: - 寻找一条增广路径。...
在二分图中,顶点集可以被分为两个互不相交的子集,边则连接这两个子集中的顶点。二分图的最大匹配是指在不违反匹配规则(即两个顶点不能同时与同一边关联)的前提下,找到包含尽可能多边的子集。 在描述中的两个...
* 森林:不相交的树结构的集合 树结构的存储: * 数组存储:使用数组来存储树结构的节点 * 链接存储:使用链表来存储树结构的节点 链链接接存储是树结构中的一种常用的存储方法,每个节点对应一个链表,链表中...
二分图的最大匹配问题是寻找二分图中最多的一对对顶点,使得每对顶点之间有一条边相连,且任意两对顶点间的边不相交。这个问题在实际中有多种应用,比如婚姻匹配、作业分配、网络路由等。 匈牙利算法是解决二分图...
2. **PathFinder(路径寻找器)**:该工具提供了一组操作来合并、相交、挖空和拆分图形。例如,"Unite"(交集)将多个路径合并为一个单一对象,"Merge"(并集)将重叠部分合并,"Offset Path"(偏移路径)创建路径的...
他们的分析在图分离器和VC维(Vapnik–Chervonenkis维数)的概念之间建立了一种联系,利用了基于匹配和不相交路径的技术。 研究论文的引言部分讨论了在大型未结构化网络中推断网络属性的困难。基本问题包括:网络的...