HDU-3998 Sequence 最多不相交上升子序列
2012-12-11 09:42
369 查看
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3998
题目大意是:给你一个数列,要你求最长的上升子序列,并且求最多有几个,且每个数只能在每个子序列中出现一次。对于第一问题很容易用DP解决,那么第二个问题,如果每个数可以出现多次的话,DP也很容易解决,但这里必须只能出现一次,否则的话就得判重,复杂度O(n3)。把问题简化一下,就是求长度为s的点不想交的路径有多少条,即每个点的容量为1(拆点),dp值相差为1的建立边,增加源汇点s、t,s向dp值为1的建立边,dp值最大的向t建立边,然后求最大流。
在网上看到这题贪心也可以搞,具体做法就是从左往右扫描,能组成最长的就吧子序列删除。这里加一个优化,用一个数组记录每个数的位置,那么复杂度只有O(n)了,,Orz。
DP+最大流:
DP+贪心(某牛代码):
题目大意是:给你一个数列,要你求最长的上升子序列,并且求最多有几个,且每个数只能在每个子序列中出现一次。对于第一问题很容易用DP解决,那么第二个问题,如果每个数可以出现多次的话,DP也很容易解决,但这里必须只能出现一次,否则的话就得判重,复杂度O(n3)。把问题简化一下,就是求长度为s的点不想交的路径有多少条,即每个点的容量为1(拆点),dp值相差为1的建立边,增加源汇点s、t,s向dp值为1的建立边,dp值最大的向t建立边,然后求最大流。
在网上看到这题贪心也可以搞,具体做法就是从左往右扫描,能组成最长的就吧子序列删除。这里加一个优化,用一个数组记录每个数的位置,那么复杂度只有O(n)了,,Orz。
DP+最大流:
//STATUS:G++_AC_15MS_1620KB #include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> #include<iostream> #include<string> #include<algorithm> #include<vector> #include<queue> #include<stack> #include<map> using namespace std; #define LL __int64 #define pii pair<int,int> #define Max(a,b) ((a)>(b)?(a):(b)) #define Min(a,b) ((a)<(b)?(a):(b)) #define mem(a,b) memset(a,b,sizeof(a)) #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 const int MAX=10100,INF=0x3f3f3f3f; const LL LLNF=0x3f3f3f3f3f3f3f3fLL; struct Edge{ int u,v,cap; }; vector<Edge> e; vector<int> g[MAX]; int p[MAX],cur[MAX],num[MAX],dp[MAX],d[MAX]; int n,s,t,mt,maxlen; void adde(int a,int b,int val){ e.push_back((Edge){a,b,val}); g[a].push_back(mt++); e.push_back((Edge){b,a,0}); g[b].push_back(mt++); } void gets() { int i,j; dp[1]=1; for(i=2;i<=n;i++){ dp[i]=1; for(j=1;j<i;j++) if(num[j]<num[i] && dp[j]>=dp[i])dp[i]=dp[j]+1; } } int augment() { int x=t,a=INF; while(x!=s){ a=Min(a,e[p[x]].cap); x=e[p[x]].u; } x=t; while(x!=s){ e[p[x]].cap-=a; e[p[x]^1].cap+=a; x=e[p[x]].u; } return a; } int isap() { int i,x,ok,min,flow=0; mem(d,0);mem(num,0); num[0]=t+1; mem(cur,0); x=s; while(d[s]<=t){ if(x==t){ flow+=augment(); x=s; } ok=0; for(i=cur[x];i<g[x].size();i++){ Edge& et=e[g[x][i]]; if(et.cap && d[x]==d[et.v]+1){ ok=1; p[et.v]=g[x][i]; cur[x]=i; x=et.v; break; } } if(!ok){ min=t; for(i=0;i<g[x].size();i++){ Edge& et=e[g[x][i]]; if(et.cap && d[et.v]<min)min=d[et.v]; } if(--num[d[x]]==0)break; num[d[x]=min+1]++; cur[x]=0; if(x!=s)x=e[p[x]].u; } } return flow; } int main() { // freopen("in.txt","r",stdin); int i,j; while(~scanf("%d\n",&n)) { mt=0; s=0,t=n<<1|1; e.clear(); for(i=0;i<=t;i++)g[i].clear(); for(i=1;i<=n;i++) scanf("%d",&num[i]); gets(); maxlen=1; for(i=2;i<=n;i++) if(dp[i]>maxlen)maxlen=dp[i]; for(i=1;i<=n;i++){ adde(i,n+i,1); for(j=i+1;j<=n;j++) if(dp[i]+1==dp[j]) adde(n+i,j,1); } for(i=1;i<=n;i++){ if(dp[i]==1)adde(s,i,1); if(dp[i]==maxlen)adde(n+i,t,1); } printf("%d\n%d\n",maxlen,isap()); } return 0; }
DP+贪心(某牛代码):
/*hdu 3998 求最长上升序列的个数 贪心 先暴力 n ^ 2 预处理出 每个dp 值,当然也可以用线段树 降低复杂度 如 1 2 3 1 1 2 3 2 3 1 2 3 现在 分别要找到 1 2 3, 从前往后, 找到了就不能在用 所以就开数组 保存每个1 2 3 的位置 , 类似于 指针扫描的做法就可以在 (o)(n) 的时间内计算出 个数*/ #include<map> #include<set> #include<cmath> #include<queue> #include<stack> #include<cstdio> #include<vector> #include<string> #include<cstring> #include<iostream> #include<algorithm> const double pi = acos(-1.0); using namespace std; #define L(x) (x << 1) #define R(x) (x << 1 | 1) #define MID int mid = (l + r) >> 1; const int maxn = 608; int dp[maxn], a[maxn], now[maxn]; int pos[maxn][maxn]; int main(){ int n; // freopen("hdu 3998.in","r",stdin); while(scanf("%d",&n)!=EOF){ for(int i = 1; i <= n; i++){ scanf("%d", a + i); } if(n == 0) {printf("0\n0\n");continue;} int s = 1; dp[1] = 1; for(int i = 2; i <= n; i++){ dp[i] = 1; for(int j = 1; j < i; j++){ if(a[i] > a[j] && dp[i] <= dp[j]){ dp[i] = dp[j] + 1; } } s = max(s, dp[i]); } for(int i = 1; i <= s; i++) pos[i][0] = 0; for(int i = 1; i <= n; i++){ pos[dp[i]][0]++; pos[dp[i]][pos[dp[i]][0]] = i; } int ans = 0; for(int i = 1; i <= s; i++) now[i] = 1; while(1){ int flag = 0, tmp; if(now[1] > pos[1][0]) break; else { tmp = pos[1][now[1]]; now[1]++; } for(int i = 2; i <= s; i++){ while(now[i] <= pos[i][0] && pos[i][now[i]] <= tmp) now[i]++; if(now[i] > pos[i][0]){ flag = 1; break; } tmp = pos[i][now[i]]; now[i]++; } if(flag) break; ans++; } printf("%d\n%d\n", s, ans); } } /* 4 3 6 2 5 */
相关文章推荐
- HDU 3998 Sequence 最长上升子序列+网络流 求不相交的最长上升子序列个数
- 多校第16场 HDU 3998 Sequence(最多不相交路径)
- hdu 3998 Sequence(DP+最大流,求最多的不相交路径)
- HDU 3998 Sequence (最长上升子序列+最大流)
- HDU 3998 Sequence(经典问题,最长上升子序列)
- hdu 3998 Sequence 求不相交最长上升自序列个数
- hdu 3998 Sequence --最长上升子序列+最大流
- HDU 3998 Sequence (最长上升子序列 + 最大流)
- hdu 3998(最长上升子序列及个数)
- hdu 3998 最大流 (最多不相交路径)
- hdu 3998 (最长上升子序列+最大流)
- hdu 3998(最长上升子序列及个数)
- HDU_3998_Sequence(最长上升子序列 + 网络流)
- 【网络流】 HDU 3998 Sequence 最多不重合路径
- HDU 3998 Sequence (最长递增子序列+最大流SAP,拆点法)经典
- HDU 1087 Super Jumping! Jumping! Jumping!(DP,上升子序列)
- HDU 1160 FatMouse's Speed (最长上升子序列+记录路径)
- hdu 1069 (最长上升子序列)
- HDU 6197 array array array(最长上升或下降子序列)
- 【HDU 1950】Bridging signals(LIS算法求最长上升子序列)