题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3998
【题意】求完全不同的最长上升子序列的个数。
【分析】dp求出最长上升子序列记为ans,然后使用最大流求解方案数。把每个点拆为两点X,Y。对于dp[i] == 1的点,连边(S,iX, inf),对于dp[i] == ans的点,连边(iY,T,inf);
(iX,iY,1)限制每个点只选一次。对于dp[j] + 1 == dp[i], j < i &&a[j] < a[i],连边(jY,iX,inf)。
跑dinic() 15ms
1 #include<stdio.h> 2 #include<string.h> 3 #include<math.h> 4 #include<algorithm> 5 #include<vector> 6 7 using namespace std; 8 9 const int maxn = 1011; 10 const int maxm = 1000010; 11 const int inf = 100000000; 12 13 int n; 14 int a[maxn]; 15 int dp[maxn]; 16 int ans; 17 18 19 struct Dinic{ 20 int node, src, dest, ne; 21 int head[maxn], work[maxn], dist[maxn],Q[maxn]; 22 int flow[maxm], pnt[maxm], nxt[maxm]; 23 void init(int _node, int _src, int _dest) 24 { 25 node = _node; 26 src = _src; 27 dest = _dest; 28 for (int i=0;i<node;i++) 29 { 30 head[i] = -1; 31 } 32 ne = 0; 33 } 34 void add(int u,int v, int c1,int c2) 35 { 36 pnt[ne] = v, flow[ne] = c1, nxt[ne] = head[u], head[u] = ne++; 37 pnt[ne] = u, flow[ne] = c2, nxt[ne] = head[v], head[v] = ne++; 38 39 } 40 bool dinic_bfs(void) 41 { 42 int i,u,v,l,r=0; 43 for(int i=0;i<node;i++) dist[i]=-1; 44 dist[Q[r++]=src]=0; 45 for(l=0;l<r;l++) for(i=head[u=Q[l]];i>=0;i=nxt[i]) 46 if(flow[i] && dist[v=pnt[i]]<0) 47 { 48 dist[Q[r++]=v]=dist[u]+1; 49 if(v==dest) return 1; 50 } 51 return 0; 52 } 53 int dinic_dfs(int u,int exp) 54 { 55 if(u==dest) return exp; 56 for(int &i=work[u],v,tmp;i>=0;i=nxt[i]) 57 if(flow[i] && dist[v=pnt[i]]==dist[u]+1 && (tmp=dinic_dfs(v,min(exp,flow[i])))>0) 58 { 59 flow[i]-=tmp; 60 flow[i^1]+=tmp; 61 return tmp; 62 } 63 return 0; 64 } 65 int dinic_flow(void) 66 { 67 int i,res=0,delta; 68 while(dinic_bfs()) 69 { 70 for(i=0;i<node;i++) work[i]=head[i]; 71 while(delta=dinic_dfs(src,inf)) res+=delta; 72 } 73 return res; 74 } 75 }Flow; 76 77 int solve() 78 { 79 int s = 0, t = 2*n + 1, nn =2*n + 2; 80 Flow.init(nn, s, t); 81 for (int i=1;i<=n;i++) 82 { 83 Flow.add(i, i+n, 1, 0); 84 if (dp[i] == ans){ 85 Flow.add(i+n, t,inf,0); 86 } 87 if (dp[i]==1){ 88 Flow.add(s, i, inf, 0); 89 continue; 90 } 91 for (int j=i-1;j>=1;j--){ 92 if (dp[j]+1 == dp[i] && a[j] < a[i]){ 93 Flow.add(j+n, i, inf, 0); 94 } 95 } 96 } 97 return Flow.dinic_flow(); 98 } 99 int main() 100 { 101 while (scanf("%d",&n)==1) 102 { 103 for (int i=1;i<=n;i++) 104 { 105 scanf("%d",&a[i]); 106 } 107 memset(dp, 0, sizeof(dp)); 108 ans = 1; 109 for (int i=1;i<=n;i++) 110 { 111 dp[i] = 1; 112 for (int j =1;j<i;j++) 113 if (a[j] < a[i]){ 114 dp[i] = max(dp[i], dp[j] + 1); 115 } 116 ans = max(ans, dp[i]); 117 } 118 printf("%d\n%d\n",ans,solve()); 119 120 121 } 122 return 0; 123 }
sap() 0 ms
1 #include<stdio.h> 2 #include<string.h> 3 #include<math.h> 4 #include<algorithm> 5 #include<vector> 6 7 using namespace std; 8 9 const int maxn = 1011; 10 const int maxm = 1000010; 11 const int inf = 100000000; 12 13 int n; 14 int a[maxn]; 15 int dp[maxn]; 16 int ans; 17 18 19 20 struct SAP{ 21 struct Edge{ 22 int v, next; 23 int w; 24 }edge[maxm]; 25 int head[maxn]; 26 27 int cnt; 28 int n, s, t;//n = t + 1; 29 30 31 int pre[maxn],cur[maxn],dis[maxn],gap[maxn]; 32 33 void add(int u,int v,int w) 34 { 35 edge[cnt].v=v; 36 edge[cnt].w=w; 37 edge[cnt].next=head[u]; 38 head[u]=cnt++; 39 edge[cnt].v=u; 40 edge[cnt].w=0; 41 edge[cnt].next=head[v]; 42 head[v]=cnt++; 43 } 44 45 int sap() 46 { 47 int flow=0,aug=inf; 48 int u; 49 bool flag; 50 for(int i=0;i<=n;i++) 51 { 52 cur[i]=head[i]; 53 gap[i]=dis[i]=0; 54 } 55 gap[s]=n; 56 u=pre[s]=s; 57 while(dis[s]<n) 58 { 59 flag=0; 60 for(int &j=cur[u];j!=-1;j=edge[j].next) 61 { 62 int v=edge[j].v; 63 if(edge[j].w>0&&dis[u]==dis[v]+1) 64 { 65 flag=1; 66 if(edge[j].w<aug) aug=edge[j].w; 67 pre[v]=u; 68 u=v; 69 if(u==t) 70 { 71 flow+=aug; 72 while(u!=s) 73 { 74 u=pre[u]; 75 edge[cur[u]].w-=aug; 76 edge[cur[u]^1].w+=aug; 77 } 78 aug=inf; 79 } 80 break; 81 } 82 } 83 if(flag) 84 continue; 85 int mindis=n; 86 for(int j=head[u];j!=-1;j=edge[j].next) 87 { 88 int v=edge[j].v; 89 if(edge[j].w>0&&dis[v]<mindis) 90 { 91 mindis=dis[v]; 92 cur[u]=j; 93 } 94 } 95 if((--gap[dis[u]])==0) 96 break; 97 gap[dis[u]=mindis+1]++; 98 u=pre[u]; 99 } 100 101 return flow; 102 103 } 104 105 void init(int __n,int __s, int __t) 106 { 107 n = __n; 108 s = __s; 109 t = __t; 110 cnt = 0; 111 memset(head,-1,sizeof(head)); 112 } 113 }Flow; 114 115 int solve() 116 { 117 int nn = 2*n + 2, s = 0, t = 2*n + 1; 118 Flow.init(nn,s,t); 119 for (int i=1;i<=n;i++) 120 { 121 Flow.add(i,i+n,1); 122 if (dp[i] == ans){ 123 Flow.add(i+n,t,inf); 124 } 125 if (dp[i]==1){ 126 Flow.add(s,i, inf); 127 continue; 128 } 129 for (int j=1;j<i;j++) 130 if (a[j] < a[i] && dp[j]+1 == dp[i]){ 131 Flow.add(j+n, i, inf); 132 } 133 } 134 return Flow.sap(); 135 } 136 int main() 137 { 138 while (scanf("%d",&n)==1) 139 { 140 for (int i=1;i<=n;i++) 141 { 142 scanf("%d",&a[i]); 143 } 144 memset(dp, 0, sizeof(dp)); 145 ans = 1; 146 for (int i=1;i<=n;i++) 147 { 148 dp[i] = 1; 149 for (int j =1;j<i;j++) 150 if (a[j] < a[i]){ 151 dp[i] = max(dp[i], dp[j] + 1); 152 } 153 ans = max(ans, dp[i]); 154 } 155 printf("%d\n%d\n",ans,solve()); 156 } 157 return 0; 158 }
本文介绍了解决HDU 3998问题的方法,该问题是求解完全不同的最长上升子序列的个数。文章详细阐述了如何使用动态规划(DP)找到最长上升子序列,再利用最大流算法(Dinic和SAP实现)计算不同子序列的数量。通过代码示例,展示了两种最大流算法的具体实现和比较。
6万+

被折叠的 条评论
为什么被折叠?



