POJ - 3436网络流_拆点+简单处理

写的第一道网络流。非板题
网络流貌似核心是在于建图的处理。
POJ-3436 :看了题解才做出来。
要求规划出合法生产线,这种形式基本就和网络流相关了。
具体题意是:有一个产品,有k个部件,给出n台机器,每台机器接受一定的半成品,输出新的半成品。最终成品是全部部件都在的成品。每个机器都有单位时间最大生产量,输入序列和输出序列,输入序列对应位是0表示不能有这个部件,1表示必须有,2表示可有可无。输出序列对应0表示没有,1表示有。
怎么建网络流的图呢?
首先是两台机器之间的边权不能是最大生产量,因为这样子是没办法限制中间节点的产量的。
考虑一种 X X X形图,左上左下右上右下都有机器,中间也有一个机器。如果上面两条边总和等于下面两条边,也就是对于中间点来说,他自身的容量就没有意义了。
所以一台机器我们拆成输入和输出两个点,两点容量即是该机器的容量。
这样子同时也方便机器间建图,机器也是从输出到输入的。
但是我们还需要开始和结束,超级源点和超级汇点,这两个点分别连接没有1的输入的机器和输出全部是1的机器,当然后者是机器来连接汇点。
接下来判断所有边,非同一机器和超级源点和超级汇点的边且流量大于0的都是有效边,记录输出即可。
建边的时候还需要注意除了同一机器边是容量,其他边容量应该取无穷大。

#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

#define ll long long
#define inf 0x3f3f3f3f

const int maxn = 1010;
struct Edge{
    int from,to;
    ll cap,flow;
};

int A[1001000],B[1001000];ll C[1001000];

struct Dinic{
    int n,tmp,s,t;
    vector<Edge>edges;
    vector<int>G[maxn];//邻接表用,存储的是边在edges中的序号
    bool vis[maxn];//BFS使用
    int d[maxn];//从起点到i的距离
    int cur[maxn];//当前弧下标

    void init(int n,int s,int t){
        this->n=n,this->s=s,this->t=t;
        edges.clear();
        for(int i=1;i<=n;i++)G[i].clear();
    }

    void AddEdge(int from,int to,ll cap){
        edges.push_back((Edge){from,to,cap,0});
        edges.push_back((Edge){to,from,0,0});
        tmp=edges.size();
        G[from].push_back(tmp-2);
        G[to].push_back(tmp-1);
    }

    bool BFS(){
        memset(vis,0,sizeof(vis));
        queue<int>q;
        q.push(s);
        d[s]=0,vis[s]=1;
        while(!q.empty()){
            int x=q.front();q.pop();
            for(int i=0;i<G[x].size();i++){
                Edge& e = edges[G[x][i]];
                if(!vis[e.to]&&e.cap>e.flow){
                    vis[e.to]=1;
                    d[e.to]=d[x]+1;
                    q.push(e.to);
                }//只考虑残量网络中的弧
            }
        }
        return vis[t];//用于判断是否能走到底。
    }

    int DFS(int x,ll a){//求出尽可能多的增广路(不是多条的意思,而是同一条尽可能多的分支)
        if(x==t||a==0)return a;//a表示的是当前最小,也就是接下来能用的不能超过a
        ll flow=0,f;
        for(int& i=cur[x];i<G[x].size();i++){//能保证一个dfs中不重复走同样的边(对于同一个节点),因为走过的边一定是满载的了。
            Edge& e = edges[G[x][i]];
            if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0){
                e.flow+=f;
                edges[G[x][i]^1].flow-=f;
                flow+=f;
                a-=f;
                if(a==0)break;//f表示从这个e.to的点开始使用的最大流。
            }
        }
        return flow;
    }

    ll Maxflow(){
        ll flow=0;
        while(BFS()){
            memset(cur,0,sizeof(cur));
            flow+=DFS(s,inf);
        }
        return flow;
    }

    void pri_ans(){
        printf("%d",Maxflow());
        int cnt=0;
        for(int i=1;i<=n;i++){
            for(int j=0;j<G[i].size();j++){
                Edge& e=edges[G[i][j]];
                if(e.flow>0){
                    if(e.from==n-1)continue;
                    if(e.to==n)continue;
                    int x=e.from,y=e.to;ll w=e.flow;
                    if(x>(n-2)/2)x-=(n-2)/2;if(y>(n-2)/2)y-=(n-2)/2;
                    if(x==y)continue;
                    ++cnt;
                    A[cnt]=x,B[cnt]=y,C[cnt]=w;
                }
            }
        }
        printf(" %d\n",cnt);
        for(int i=1;i<=cnt;i++)
            printf("%d %d %lld\n",A[i],B[i],C[i]);
    }
}dc;

vector<int>Gi[maxn],Go[maxn];

int main(){
    int p,n;
    while(cin>>p>>n){
        int s=2*n+1,t=2*n+2;
        dc.init(2*n+2,s,t);
        FOR(i,1,n){
            Gi[i].clear(),Go[i].clear();
            ll x;int y;
            scanf("%lld",&x);
            dc.AddEdge(i,i+n,x);
            FOR(j,1,p){
                scanf("%d",&y);
                Gi[i].push_back(y);
            }
            FOR(j,1,p){
                scanf("%d",&y);
                Go[i].push_back(y);
            }
        }
        FOR(i,1,n){
            FOR(j,1,n)if(i!=j){
                int ok=1;
                for(int k=0;k<p;k++){
                    if(Gi[j][k]==2)continue;
                    if(Gi[j][k]==1){
                        if(Go[i][k]==0)ok=0;
                    }
                    if(Gi[j][k]==0){
                        if(Go[i][k]==1)ok=0;
                    }
                }
                if(ok)dc.AddEdge(i+n,j,inf);
            }
        }
        FOR(i,1,n){
            int ok;
            ok=1;
            FOR(j,0,p-1){
                if(Gi[i][j]==1)ok=0;
            }
            if(ok)dc.AddEdge(2*n+1,i,inf);
            ok=1;
            FOR(j,0,p-1){
                if(Go[i][j]==0)ok=0;
            }
            if(ok)dc.AddEdge(i+n,2*n+2,inf);
        }
        dc.pri_ans();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
POJ - 3616是一个题目,题目描述如下: 给定一组区间,每个区间有一个权重,要求选择一些区间,使得这些区间的右端点都小于等于k,并且权重之和最大。请问最大的权重和是多少? 解决这个问题的思路是使用动态规划。首先,将区间按照左端点从小到大进行排序。然后,定义一个dp数组,dp[i]表示右端点小于等于i的所有区间所能得到的最大权重。 接下来,遍历每一个区间,对于每个区间i,将dp[i]初始化为区间i的权重。然后,再遍历i之前的每个区间j,如果区间j的右端点小于等于k,并且区间j的权重加上区间i的权重大于dp[i],则更新dp[i]为dp[j]加上区间i的权重。 最后,遍历整个dp数组,找到最大的权重和,即为所求的答案。 下面是具体的代码实现: ```cpp #include <cstdio> #include <cstring> #include <algorithm> using namespace std; struct interval{ int start, end, weight; }; interval intervals[10005]; int dp[10005]; int n, m, k; bool compare(interval a, interval b) { if (a.start == b.start) { return a.end < b.end; } else { return a.start < b.start; } } int main() { while(~scanf("%d %d %d", &n, &m, &k)) { memset(dp, 0, sizeof dp); for (int i = 0; i < m; i++) { scanf("%d %d %d", &intervals[i].start, &intervals[i].end, &intervals[i].weight); } sort(intervals, intervals + m, compare); for (int i = 0; i < m; i++) { dp[i] = intervals[i].weight; for (int j = 0; j < i; j++) { if (intervals[j].end <= k && dp[j] + intervals[i].weight > dp[i]) { dp[i] = dp[j] + intervals[i].weight; } } } int maxWeight = 0; for (int i = 0; i < m; i++) { maxWeight = max(maxWeight, dp[i]); } printf("%d\n", maxWeight); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值