网络流24题 网络流模型训练 持续更新!?

1、1736: 飞行员配对方案问题

这一题基本上不用多说,就是一题二分图加方案输出;

我先试着用二分图求一发:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<algorithm>
#define MAX_V 5500
using namespace std;
int book[MAX_V],girl[MAX_V],color[MAX_V];
vector<int> G[MAX_V];
int K,N,M;
bool serch(int x){
    int i,j;
    for(i=0;i<G[x].size();i++){
        if(book[G[x][i]]==0){
            book[G[x][i]]=1;
            if(girl[G[x][i]]==0||serch(girl[G[x][i]])){
                girl[G[x][i]]=x;
                ///printf("___ %d\n", x);
                return true;
            }
        }
    }
    return false;
}
int main(){
    int i,j,k,l;
    int x,y,ans;
    scanf("%d %d",&M,&N);
    ans=0;
    N-=M;
    fill(girl,girl+MAX_V,0);
    for(i=0;i<MAX_V;i++)
        G[i].clear();
    while(1){
        scanf("%d %d",&x,&y);
        y-=M;
        if(x==-1||y==-1)
            break;
        G[x].push_back(y);
    }
    for(i=1;i<=M;i++){
        fill(book,book+MAX_V,0);
        if(serch(i))
            ans++;
    }
    printf("%d\n",ans);
    for(int i=1;i<=N; ++i){
        if(girl[i])
            printf("%d %d\n", girl[i], i+M);
    }
    return 0;
}

  

接着用了网络流来了一发,设置一个超级源点S, 和一个超级汇点T,将外籍飞行员1~m连接上S,流量为1;英国飞行员m+1~n连接一条流量为1的边至T;其他按照题目输入给边连接:

#include<queue>
#include<stdio.h>
#include<string.h>
using namespace std;
const int maxn = 1005;
const int maxm = 1e6+7;
const int inf  = 0x3f3f3f3f;
typedef long long LL;
int t, n, m;
struct Edge {
    int to, w, next;
} edge[maxm];
int first[maxn], cur[maxn], sign, dist[maxn], pre[maxn];
void init() {
    memset(first, -1, sizeof(first));
    sign = 0;
}
void add_edge(int u,int v,int w) {
    edge[sign].to = v, edge[sign].w = w;
    edge[sign].next = first[u], first[u] = sign++;
    edge[sign].to = u, edge[sign].w = 0;
    edge[sign].next = first[v], first[v] = sign++;
}
bool bfs(int s,int t) {
    memset(dist, -1, sizeof(dist));
    queue<int>que;
    que.push(s), dist[s] = 0;
    while(!que.empty()) {
        int now = que.front();
        que.pop();
        if(now==t) return 1;
        for(int i = first[now]; ~i; i = edge[i].next) {
            int to = edge[i].to, ww = edge[i].w;
            if(dist[to] == -1 && ww > 0) {
                dist[to] = dist[now] + 1;
                que.push(to);
            }
        }
    }
    return 0;
}
int dfs(int s, int t, int max_flow) {
    if(s == t) return max_flow;
    for(int &i = cur[s]; ~i; i = edge[i].next) {
        int to = edge[i].to, ww = edge[i].w;
        if(dist[to] == dist[s] + 1 && ww > 0) {
            int flow = dfs(to, t, min(max_flow, ww));
            if(flow > 0) {
                edge[i].w -= flow;
                edge[i^1].w += flow;
                return flow;
            }
        }
    }
    return 0;
}
int dinic(int s,int t) {
    int ans = 0;
    while(bfs(s, t)) {
        for(int i = 1; i <= n; i ++ )
            cur[i] = first[i];
        ans += dfs(s, t, inf);
    }
    return ans;
}
int main() {
    int u, v, w;
    scanf("%d %d", &m, &n);
    init();
    int S=n+1, T=n+2;
    for(int i=1; i<=m; ++i) add_edge(S, i, 1);
    for(int i=m+1; i<=n; ++i) add_edge(i, T, 1);
    while(1){
        scanf("%d %d", &u, &v);
        w=1;
        if(u==-1&&v==-1)
            break;
        add_edge(u, v, w);
    }
    n+=2;
    printf("%d\n", dinic(S, T));
    for(int i=m+1; i<=n-2; ++i){
        for(int j=first[i]; ~j; j=edge[j].next){
            if(i!=T&&edge[j].to!=T&&edge[j].w)
                printf("%d %d\n", min(i, edge[j].to), max(i, edge[j].to));
        }
    }
    puts("");
    return 0;
}

  

 

2、1740: 圆桌问题

emmmm!!!!

这题。。。也是一个基础的网络流问题;

显然每个单位只能在一张桌子坐一个人,也就是每个单位有且仅有1的流量指向桌子,只要知道了这个就简单了;

对每个单位建立一条到超级源点S的边,流量为每个单位的人数;

对每张桌子建立一条到超级汇点的边,流量为桌子容量;

桌子和单位按照刚刚那样建立边;

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define inf 0x3fffffff
using namespace std;
const int maxn=555, maxm=1e6+7;
int first[maxn],sign,cur[maxn];
int s,t,d[maxn];
int mp[maxn][maxn];
struct node{
    int to,w,next;
}edge[maxm];
void init(){
    memset(first,-1,sizeof(first));
    sign=0;
}
void add_edge(int u,int v,int w){
    edge[sign].to=v;
    edge[sign].w=w;
    edge[sign].next=first[u];
    first[u]=sign++;
 
    edge[sign].to=u;
    edge[sign].w=0;
    edge[sign].next=first[v];
    first[v]=sign++;
}
int bfs(){
    queue<int>q;
    memset(d,0,sizeof(d));
    d[s]=1;
    q.push(s);
    while(!q.empty()){
        int top=q.front();
        q.pop();
        for(int i=first[top];~i;i=edge[i].next){
            int to=edge[i].to;
            if(edge[i].w>0&&d[to]==0){
                d[to]=d[top]+1;
                if(to==t)
                    return 1;
                q.push(to);
            }
        }
    }
    return d[t]!=0;
}
int dfs(int top,int flow){
    if(top==t)
        return flow;
    int ans=0,x=0;
    for(int i=cur[top];~i;i=edge[i].next){
        int to=edge[i].to;
        if(edge[i].w>0&&d[to]==d[top]+1){
            x=dfs(to,min(flow-ans,edge[i].w));
            edge[i].w-=x;
            edge[i^1].w+=x;
            if(edge[i].w)
                cur[top] = i;
            ans+=x;
            if(ans==flow)
                return flow;
        }
    }
    if(ans==0)
        d[top]=0;
    return ans;
}
int dinic(int n){
    int ans=0;
    while(bfs()){
        for(int i=0;i<=n;i++)
            cur[i]=first[i];
        ans+=dfs(s,inf);
    }
    return ans;
}
int peo[1007], tab;
vector<int> vv[1007];
int main(){
    int n, m, sum=0;
    scanf("%d%d", &n, &m);
    init();
    s=0,t=n+m+1;
    for(int i=1; i<=n; ++i){
        scanf("%d", peo+i), sum+=peo[i];
        add_edge(s, i, peo[i]);
    }
    for(int i=1; i<=n; ++i)
        for(int j=n+1; j<=m+n; ++j)
            add_edge(i, j, 1);
    for(int i=n+1; i<=m+n; ++i){
        scanf("%d", &tab);
        add_edge(i, t, tab);
    }
    int x,y;
    int ans=dinic(t);
    if(ans==sum){
        puts("1");
        for(int i=n+1; i<=m+n; ++i){
            for(int j=first[i]; ~j; j=edge[j].next){
                if(edge[j].to!=t&&edge[j].w)
                    vv[edge[j].to].push_back(i-n);
            }
        }
        for(int i=1; i<=n; ++i){
            if(vv[i].size()){
                printf("%d", vv[i][0]);
                for(int j=1; j<vv[i].size(); ++j)
                    printf(" %d", vv[i][j]);
                puts("");
            }
        }
    }else{
        puts("0");
    }
    return 0;
}

  

 

3、魔术球问题

这一题一开始除了建边以外没有想法,问了大佬才知道是最小路径覆盖问题;

最小路径覆盖:将原来的有向无环图G=(V,E),n=|V|。把G中每一个点x拆成编号为x和x+n的两个点。建立一张新的二分图。1~n为二分图的左部,n+1~2*n为右部分,对于原图的每条有向边(x, y),将其建立成(x,y+n)的连边,得到G图的拆点二分图,记为G2;那么最小路径覆盖条数=n-G2的最大匹配数;

这题就可以变成问至少多少个点可以被n条路径覆盖;

将每个点和之前比他小的点连上一条流量为1的边,且连接所有的可以和这个点值相加为完全平方数的点值相连,流量为1;

因为起点可以是前k个点里的任意一个点,所以说超级源点S要和1~k每个点连接一条边,流量为1;

然后再按照之前的建图规则将1+MAX~k+MAX(MAX:最大的k设置1567~无穷大区间里任意一个就可以了#滑稽)的点连接上超级汇点T,流量为1;

将dinic(我是用这个)初始化一次,之后加入一个点就加边一次增广一次;

直到当前点的数量k-增广出来的值大于题目输入的n时我们就可以知道k-1的时候是路径为n时的最大可容纳点;

详细的见代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define inf 0x3fffffff
using namespace std;
const int maxn=5007, maxm=1e6+7;
int first[maxn],sign,cur[maxn];
int s,t,d[maxn];
int mp[maxn][maxn];
struct node{
    int to,w,next;
}edge[maxm];
int sqr[89];
void init(){
    memset(first,-1,sizeof(first));
    sign=0;
}
void add_edge(int u,int v,int w){
    edge[sign].to=v;
    edge[sign].w=w;
    edge[sign].next=first[u];
    first[u]=sign++;
 
    edge[sign].to=u;
    edge[sign].w=0;
    edge[sign].next=first[v];
    first[v]=sign++;
}
int bfs(){
    queue<int>q;
    memset(d,0,sizeof(d));
    d[s]=1;
    q.push(s);
    while(!q.empty()){
        int top=q.front();
        q.pop();
        for(int i=first[top];~i;i=edge[i].next){
            int to=edge[i].to;
            if(edge[i].w>0&&d[to]==0){
                d[to]=d[top]+1;
                if(to==t)
                    return 1;
                q.push(to);
            }
        }
    }
    return d[t]!=0;
}
int dfs(int top,int flow){
    if(top==t)
        return flow;
    int ans=0,x=0;
    for(int i=cur[top];~i;i=edge[i].next){
        int to=edge[i].to;
        if(edge[i].w>0&&d[to]==d[top]+1){
            x=dfs(to,min(flow-ans,edge[i].w));
            edge[i].w-=x;
            edge[i^1].w+=x;
            if(edge[i].w)
                cur[top] = i;
            ans+=x;
            if(ans==flow)
                return flow;
        }
    }
    if(ans==0)
        d[top]=0;
    return ans;
}
int dinic(int n){
    int ans=0;
    while(bfs()){
        for(int i=0;i<=n;i++)
            cur[i]=first[i];
        ans+=dfs(s,inf);
    }
    return ans;
}
void add_point(int i, int &n){
    register int j;
    for(j=1; sqr[j]<2*i; ++j)
        if(sqr[j]>i) add_edge(sqr[j]-i, i+1569, 1);
    add_edge(s, i, 1);
    add_edge(i+1569, t, 1);
}
int fa[maxn], vis[maxn];
int main(){
    register int n, i, j, now, sav;
    for(i=1; i<78; ++i)
        sqr[i]=i*i;
    scanf("%d", &n);
    init();
    s=0,t=1569*2+1;
    int ans=0;
    for(i=1; ; ++i){
        add_point(i, n);
        ans+=dinic(t);
        if(i-ans>n)
            break;
    }
    now=i-1;
    printf("%d\n", now);
    for(i=1; i<=now; ++i){
        for(sav=i, j=first[i]; ~j; j=edge[j].next)
            if(edge[j].to!=s&&edge[j^1].w)
                fa[sav]=edge[j].to-1569, sav=edge[j].to-1569;
    }
    for(i=1; i<=now; ++i){
        if(!vis[i]){
            printf("%d", i);
            vis[i]=1;
            for(j=fa[i]; j; j=fa[j]){
                printf(" %d", j);
                vis[j]=1;
            }
            puts("");
        }
    }
    return 0;
}

  

这题ac后可以瞎jb优化

用别人dp的ac代码打个n=1~55的表存在loc数组里,将1~loc[n]个点全部加入图里建图,跑一次dinic得到答案,2ms;

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define inf 0x3fffffff
using namespace std;
const int maxn=5007, maxm=1e6+7;
int first[maxn],sign,cur[maxn];
int s,t,d[maxn];
int mp[maxn][maxn];
struct node{
    int to,w,next;
}edge[maxm];
int sqr[89], loc[57]={0,1,3,7,11,17,23,31,39,49,59,71,83,97,111,127,143,161,179,199,219,241,263,287,311,337,363,391,419,449,479,511,543,577,611,647,683,721,759,799,839,881,923,967,1011,1057,1103,1151,1199,1249,1299,1351,1403,1457,1511,1567};
void init(){
    memset(first,-1,sizeof(first));
    sign=0;
}
void add_edge(int u,int v,int w){
    edge[sign].to=v;
    edge[sign].w=w;
    edge[sign].next=first[u];
    first[u]=sign++;
 
    edge[sign].to=u;
    edge[sign].w=0;
    edge[sign].next=first[v];
    first[v]=sign++;
}
int bfs(){
    queue<int>q;
    memset(d,0,sizeof(d));
    d[s]=1;
    q.push(s);
    while(!q.empty()){
        int top=q.front();
        q.pop();
        for(int i=first[top];~i;i=edge[i].next){
            int to=edge[i].to;
            if(edge[i].w>0&&d[to]==0){
                d[to]=d[top]+1;
                if(to==t)
                    return 1;
                q.push(to);
            }
        }
    }
    return d[t]!=0;
}
int dfs(int top,int flow){
    if(top==t)
        return flow;
    int ans=0,x=0;
    for(int i=cur[top];~i;i=edge[i].next){
        int to=edge[i].to;
        if(edge[i].w>0&&d[to]==d[top]+1){
            x=dfs(to,min(flow-ans,edge[i].w));
            edge[i].w-=x;
            edge[i^1].w+=x;
            if(edge[i].w)
                cur[top] = i;
            ans+=x;
            if(ans==flow)
                return flow;
        }
    }
    if(ans==0)
        d[top]=0;
    return ans;
}
int dinic(int n){
    int ans=0;
    while(bfs()){
        for(int i=0;i<=n;i++)
            cur[i]=first[i];
        ans+=dfs(s,inf);
    }
    return ans;
}
int fa[maxn], vis[maxn];
int main(){
    register int n, i, j, now, sav;
    for(i=1; i<78; ++i)
        sqr[i]=i*i;
    scanf("%d", &n);
    init();
    s=0,t=1567*2+1;
    int ans=0;
    for(i=1; i<=loc[n]; ++i){
        for(j=1; sqr[j]<2*i; ++j)
            if(sqr[j]>i) add_edge(sqr[j]-i, i+1567, 1);
        add_edge(s, i, 1);
        add_edge(i+1567, t, 1);
    }
    ans+=dinic(t);
    now=i-1;
    printf("%d\n", loc[n]);
    for(i=1; i<=now; ++i){
        for(sav=i, j=first[i]; ~j; j=edge[j].next)
            if(edge[j].to!=s&&edge[j^1].w)
                fa[sav]=edge[j].to-1567, sav=edge[j].to-1567;
    }
    for(i=1; i<=now; ++i){
        if(!vis[i]){
            printf("%d", i);
            vis[i]=1;
            for(j=fa[i]; j; j=fa[j]){
                printf(" %d", j);
                vis[j]=1;
            }
            puts("");
        }
    }
    return 0;
}

 

4、太空飞行计划问题

这一题是最大权闭合子图的问题,最大权闭合子图问题可以转化成最小割问题(详细看篇博客:https://www.cnblogs.com/wuyiqi/archive/2012/03/12/2391960.html)

那么这一题就是最大流问题;

而且根据上面看的博客我们得到了建图的方案,即对超级源点S与每一个实验建立一条容量为投资p的边,再对每一个仪器建立一条到超级汇点T的容量为仪器价格的边,其他实验到仪器的边建为INF的流量;

现在我们可以求答案了,也就是总实验的投资减去仪器的价格;

但是方案的输出还不明了,但是我们仔细一想,这不是最小割问题吗!也就是说我们可以求他的割集里包含的点,这个点就是最小割的答案;

最小割定义:给定一个网络G=(V, E),源点S,汇点T。若一个边集E` ⊆ E被删除去之后,源点和汇点不再连通,则称该边集为网络的割。边的容量之和最小的割成为最小割;

那么怎么求割边的点集是啥呢!dinic的实现方法是每次分层以后多次增广,也就是说每次增广基本上都是在削减残余网络里的剩余正向流量,即最后一次成功到达汇点T的BFS一定是最小的割集,也就是我们找到了最小割的割集,那么同理,最后一次经过的点,就是最小割包含的点集,也就是我们取最大权闭合子图的方案,我们可以用dinic的dis数组来判断;剩下的看代码。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define inf 0x3fffffff
using namespace std;
const int maxn=5007, maxm=1e6+7;
int first[maxn], sign, cur[maxn];
int s,t,dis[maxn];
int mp[maxn][maxn], N, M;
struct node{
    int to,w,next;
}edge[maxm];
int inr[maxn], anss[maxn];
void init(){
    memset(first,-1,sizeof(first));
    sign=0;
}
void write(int x){
    if(x==0){putchar(48);return;}
    int len=0,dg[20];
    while(x>0){dg[++len]=x%10;x/=10;}
    for(int i=len;i>=1;i--)putchar(dg[i]+48);
}
void add_edge(int u,int v,int w){
    edge[sign].to=v;
    edge[sign].w=w;
    edge[sign].next=first[u];
    first[u]=sign++;

    edge[sign].to=u;
    edge[sign].w=0;
    edge[sign].next=first[v];
    first[v]=sign++;
}
int bfs(){
    queue<int>q;
    memset(dis,0,sizeof(dis));
    dis[s]=1;
    q.push(s);
    while(!q.empty()){
        int top=q.front();
        q.pop();
        for(int i=first[top];~i;i=edge[i].next){
            int to=edge[i].to;
            if(edge[i].w>0&&dis[to]==0){
                dis[to]=dis[top]+1;
                if(to==t)
                    return 1;
                q.push(to);
            }
        }
    }
    return dis[t]!=0;
}
int dfs(int top,int flow){
    if(top==t)
        return flow;
    int ans=0,x=0;
    for(int i=cur[top];~i;i=edge[i].next){
        int to=edge[i].to;
        if(edge[i].w>0&&dis[to]==dis[top]+1){
            x=dfs(to,min(flow-ans,edge[i].w));
            edge[i].w-=x;
            edge[i^1].w+=x;
            if(edge[i].w)
                cur[top] = i;
            ans+=x;
            if(ans==flow)
                return flow;
        }
    }
    if(ans==0)
        dis[top]=0;
    return ans;
}
int dinic(int n){
    int ans=0;
    while(bfs()){
        for(int i=0;i<=n;i++)
            cur[i]=first[i];
        ans+=dfs(s,inf);
    }
    return ans;
}
int main(){
    register int i, ans, sum=0, cnt, n, m;
    scanf("%d%d", &m, &n);
    N=n, M=m;
    init();
    s=0,t=m+n+1;
    for(i=1; i<=m; i++){
        int x;
        scanf("%d", &x);
        sum+=x;
        add_edge(s, i, x);
        char ch=getchar();
        while((ch=getchar())!='\n')
        {
            x=ch-'0';
            while((ch=getchar())&&ch>='0'&&ch<='9')
                x=x*10+ch-'0';
            add_edge(i, x+m, maxn);
            if(ch=='\n') break;
        }
    }
    for(i=1; i<=n; ++i)
        scanf("%d", &cnt), add_edge(i+m, t, cnt);
    ans=dinic(t);
    cnt=1;
    for(i=1; i<=m; ++i)
        if(dis[i]) anss[cnt++]=i;///dis不是0则有去过别的点 那么就是割集的点集之一
    for(i=1; i<cnt-1; ++i){
        printf("%d ", anss[i]);
    }
    printf("%d\n", anss[cnt-1]);
    cnt=1;
    for(i=m+1; i<=n+m; ++i)
        if(dis[i]) anss[cnt++]=i-m;
    for(i=1; i<cnt-1; ++i){
        printf("%d ", anss[i]);
    }
    printf("%d\n", anss[cnt-1]);
    printf("%d\n", sum-ans);
    return 0;
}

  

5、最小路径覆盖问题

emmmmm!!!这题。。。字面意思。。。魔术球问题弱化版。。。其实应该先写这题的。。。。

直接贴代码了,模型已经见过了!!!

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define inf 0x3fffffff
using namespace std;
const int maxn=5007, maxm=1e6+7;
int first[maxn],sign,cur[maxn];
int s,t,d[maxn];
int mp[maxn][maxn];
struct node{
    int to,w,next;
}edge[maxm];
void init(){
    memset(first,-1,sizeof(first));
    sign=0;
}
void add_edge(int u,int v,int w){
    edge[sign].to=v;
    edge[sign].w=w;
    edge[sign].next=first[u];
    first[u]=sign++;
 
    edge[sign].to=u;
    edge[sign].w=0;
    edge[sign].next=first[v];
    first[v]=sign++;
}
int bfs(){
    queue<int>q;
    memset(d,0,sizeof(d));
    d[s]=1;
    q.push(s);
    while(!q.empty()){
        int top=q.front();
        q.pop();
        for(int i=first[top];~i;i=edge[i].next){
            int to=edge[i].to;
            if(edge[i].w>0&&d[to]==0){
                d[to]=d[top]+1;
                if(to==t)
                    return 1;
                q.push(to);
            }
        }
    }
    return d[t]!=0;
}
int dfs(int top,int flow){
    if(top==t)
        return flow;
    int ans=0,x=0;
    for(int i=cur[top];~i;i=edge[i].next){
        int to=edge[i].to;
        if(edge[i].w>0&&d[to]==d[top]+1){
            x=dfs(to,min(flow-ans,edge[i].w));
            edge[i].w-=x;
            edge[i^1].w+=x;
            if(edge[i].w)
                cur[top] = i;
            ans+=x;
            if(ans==flow)
                return flow;
        }
    }
    if(ans==0)
        d[top]=0;
    return ans;
}
int dinic(int n){
    int ans=0;
    while(bfs()){
        for(int i=0;i<=n;i++)
            cur[i]=first[i];
        ans+=dfs(s,inf);
    }
    return ans;
}
int fa[maxn], vis[maxn];
int main(){
    register int n, i, j, now, sav, m, u, v;
    scanf("%d%d", &n, &m);
    init();
    s=0,t=2*n+1;
    for(i=1; i<=n; ++i){
        add_edge(s, i, 1);
        add_edge(i+n, t, 1);
    }
    while(m--){
        scanf("%d%d", &u, &v);
        add_edge(u, v+n, 1);
    }
    int ans=dinic(t);
    now=i-1;
    for(i=1; i<=now; ++i){
        for(sav=i, j=first[i]; ~j; j=edge[j].next)
            if(edge[j].to!=s&&edge[j^1].w)
                fa[sav]=edge[j].to-n, sav=edge[j].to-n;
    }
    for(i=1; i<=now; ++i){
        if(!vis[i]){
            printf("%d", i);
            vis[i]=1;
            for(j=fa[i]; j; j=fa[j]){
                printf(" %d", j);
                vis[j]=1;
            }
            puts("");
        }
    }
    printf("%d\n", n-ans);
    return 0;
}

  

6、最长上升子序列问题

这个问题一开始完全想不到解法,查了才知道是最多不相交路径数的模型;

这题的建图其实就是在dp已经完成的基础上建立的;

第一问dp结束;

首先,我们知道我们对于第二问是计算从给定的序列中最多可取出多少个长度为maxn的递增子序列(每个元素都只能用一次,所以说流量很明显全是1)。

那么既然是求子序列个数,很明显要是一条一条的长度为maxn的路径;也就是说要建立长度为maxn的路径,那么很明显了,dp[i]为1的很明显是终点/起点,然后dp[i]为maxn的点很明显是起点/终点,然后对于每个满足j<i&&a[j]<a[i]&&dp[j]+1==dp[i]条件的i,j点建立一条边,如果终点你取dp[i]==1那么就从i->j建立边,如果终点你取dp[i]==maxn那么就从j->i建立边。然后流流流结束。

第三问的话,我是直接拆了图重新建,只要改s、t位置和1、n的连接流量为INF(前提是有那个边);

再跑一变流流流结束;

这里有一点要注意,每次DFS增广出来的答案如果是INF,那么说明有长度为1的序列,这个序列只能算一次;

具体看代码:

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=1007, MAXM=1e6+7, INF=0x3fffffff;
int first[MAXN],sign,cur[MAXN];
int s,t,dis[MAXN];
struct node{
    int to,w,next;
}edge[MAXM];
void init(){
    memset(first,-1,sizeof(first));
    sign=0;
}
void add_edge(int u,int v,int w){
    edge[sign].to=v;
    edge[sign].w=w;
    edge[sign].next=first[u];
    first[u]=sign++;
 
    edge[sign].to=u;
    edge[sign].w=0;
    edge[sign].next=first[v];
    first[v]=sign++;
}
int bfs(){
    queue<int>q;
    memset(dis, 0, sizeof(dis));
    dis[s]=1;
    q.push(s);
    while(!q.empty()){
        int top=q.front();
        q.pop();
        for(int i=first[top];~i;i=edge[i].next){
            int to=edge[i].to;
            if(edge[i].w>0&&dis[to]==0){
                dis[to]=dis[top]+1;
                if(to==t)
                    return 1;
                q.push(to);
            }
        }
    }
    return dis[t]!=0;
}
int dfs(int top,int flow){
    if(top==t)
        return flow;
    int ans=0,x=0;
    for(int i=cur[top];~i;i=edge[i].next){
        int to=edge[i].to;
        if(edge[i].w>0&&dis[to]==dis[top]+1){
            x=dfs(to,min(flow-ans,edge[i].w));
            edge[i].w-=x;
            edge[i^1].w+=x;
            if(edge[i].w)
                cur[top] = i;
            ans+=x;
            if(ans==flow)
                return flow;
        }
    }
    if(ans==0) dis[top]=0;
    return ans;
}
int dinic(int n){
    int ans=0, sum;
    while(bfs()){
        for(int i=0;i<=n;i++)
            cur[i]=first[i];
        sum=dfs(s,INF);
        if(sum==INF)///判断处
            sum=1;
        ans+=sum;
    }
    return ans;
}
int dp[MAXN], a[MAXN];
void create(int flow, int &n, int &maxn){///建边
    init();
    register int i, j;
    for(i=1; i<=n; ++i){
        for(j=1; j<i; ++j){
            if(dp[j]+1==dp[i]&&a[j]<a[i]){
                add_edge(i, j, 1);
            }
        }
        if(dp[i]==1){
            add_edge(i, t, flow);
        }
        maxn=max(maxn, dp[i]);
    }
    for(i=1; i<=n; ++i){
        if(dp[i]==maxn){
            add_edge(s, i, flow);
        }
    }
}
int main(){
    register int i, j, n, maxn=1;
    scanf("%d", &n);
    s=0, t=n+1;
    for(i=1; i<=n; ++i){
        dp[i]=1;
        scanf("%d", &a[i]);
        for(j=1; j<i; ++j){
            if(a[i]>a[j]){
                dp[i]=max(dp[i], dp[j]+1);
            }
        }
        maxn=max(maxn, dp[i]);
    }
 
    printf("%d\n", maxn);
    create(1, n, maxn);
    printf("%d\n", dinic(t));
    create(INF, n, maxn);
    printf("%d\n", dinic(t));
    return 0;
}

  

转载于:https://www.cnblogs.com/DCD112358/p/9409690.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园建设是在国家政策推动下,为深化教育改革、提升教育质量和管理效率而提出的重要项目。该项目旨在通过信息化手段,解决传统教育中存在的资源分散、管理混乱等问,实现教育资源的高效利用和教学质量的全面提升。 目前,教育信息化虽取得一定进展,但面临“孤岛架构”的挑战,包括硬件资源无法共享、数据孤岛、应用孤岛等问,导致资源浪费和管理效率低下。为此,智慧校园的建设目标聚焦于家校沟通便捷化、校园管理科学化、校园生活轻松化、课堂教学互动化和校园设施智能化,以提高教学效率和学生学习体验。 智慧校园的核心价值在于构建先进的网络教学平台和管理信息系统,实现教学资源的高效配置和利用,促进师生互动,提高管理效率,降低成本,构建健康高雅的生活环境。解决方案涵盖综合应用平台规划、系统架构设计、媒体发布、数字会议系统等,通过后台服务层、基础接入层和用户接入层的有机结合,实现智慧校园的全面功能。 智慧校园管理平台作为核心组成部分,提供模块化体系,包括公开课、直播、教学资源等23大应用,支持与第三方接口对接,实现多级管理。电教预约管理平台通过移动端APP或web后台简化预约程,提高教室和会议室资源利用率,支持会议预订、审批、信息发布和环境管控。 教育录播系统和云平台支持教师制作和分享优质教学资源,进行在线组卷和评卷,同时提供学生应用,如高清视频录制、在线直播和互动交,促进教学资源的共享和教育均衡化发展。这些系统的整合应用,将极大地推动教育信息化进程,实现教育资源的最大化利用和教育质量的全面提升。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值