[codevs1906]最长递增子序列(dp+dinic)

来源:互联网 发布:js皮肤哪个好 编辑:程序博客网 时间:2024/06/15 11:20

题目:

我是超链接

题解:

拆点 
把一个点拆成两个点来保证每个点用的次数。 
关系的转移 
dp之后发现必须满足i在j之前,i的值小于等于j的值并且dp出来的长度i+1=j的时候才能从i向j连边。 
连向源点、汇点的边注意一下,并且可以当做起点和当做终点的点的边的容量注意一下,可以控制使用的次数。

代码:

#include <cstdio>#include <queue>#include <cstring>#include <iostream>#define INF 300000#define N 300000using namespace std;int f[N+5],next[N*2+5],point[N*2+5],v[N*2+5],remind[N*2+5],cur[N*2+5],tot=-1,deep[N*2+5],a[N*2+5],make[N*2+5];int b[N+5];bool c[N*2+5];void addline(int x,int y,int bb){++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y; remind[tot]=bb;++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x; remind[tot]=0;}bool bfs(int s,int t){queue <int> q;for (int i=s;i<=t;i++)  cur[i]=point[i];memset(deep,0x7f,sizeof(deep));    deep[s]=0;    q.push(s);    while (!q.empty())    {    int x=q.front(); q.pop();    for (int i=point[x];i!=-1;i=next[i])      if (remind[i] && deep[v[i]]>INF)        q.push(v[i]),deep[v[i]]=deep[x]+1;} return deep[t]<INF;}int dfs(int now,int t,int limit){if (now==t || !limit) return limit;int flow=0,f;for (int i=cur[now];i!=-1;i=next[i]){cur[now]=i;if (deep[v[i]]==deep[now]+1 && (f=dfs(v[i],t,min(limit,remind[i])))){flow+=f;limit-=f;remind[i]-=f;remind[i^1]+=f;make[now]=v[i];if (!limit) break;}}return flow;}int dinic(int s,int t){int ans=0;while (bfs(s,t))  ans+=dfs(s,t,INF);return ans;}int main(){int i,n,j;memset(point,-1,sizeof(point));memset(next,-1,sizeof(next));    memset(b,0,sizeof(b));scanf("%d",&n);for (int i=1;i<=n;i++) scanf("%d",&a[i]);    int maxx=1;b[1]=1;    for (i=2;i<=n;i++)      {      int maxn=0;      for (j=i-1;j>=1;j--)        if (a[i]>=a[j] && b[j]>maxn)        maxn=b[j];b[i]=maxn+1;maxx=max(maxx,b[i]);  }printf("%d\n",maxx);if (maxx==1)  printf("%d\n%d",n,n);else  {  int i,j;  for (i=2;i<=n;++i)      for (j=1;j<i;++j)        if (a[i]>=a[j]&&b[i]==b[j]+1)          addline(j+n,i,1);for (i=1;i<=n;i++){if (b[i]==1)                                              addline(0,i,1);    addline(i,i+n,1);}    for (i=n+1;i<=2*n;i++)  if (b[i-n]==maxx)    addline(i,n*2+1,1);   int ans=dinic(0,n*2+1);    printf("%d\n",ans);    memset(point,-1,sizeof(point));memset(next,-1,sizeof(next));tot=-1;for (int i=2;i<=n;++i)      for (int j=1;j<i;++j)        if (a[i]>=a[j]&&b[i]==b[j]+1)          addline(j+n,i,1);for (i=1;i<=n;i++){if (b[i]==1)                                              addline(0,i,INF);    addline(i,i+n,INF);}    for (i=n+1;i<=2*n;i++)  if (b[i-n]==maxx)    addline(i,n*2+1,INF); int anss=dinic(0,n*2+1);printf("%d\n",anss);   }} 

总结:1.保证每个点只流一次的方法:每个点连相应的自己容量为1

0 0