1. 程式人生 > >【文文殿下】網路流24題計劃

【文文殿下】網路流24題計劃

飛行員配對方案問題

題目背景

第二次世界大戰時期..

題目描述

英國皇家空軍從淪陷國徵募了大量外籍飛行員。由皇家空軍派出的每一架飛機都需要配備在航行技能和語言上能互相配合的2 名飛行員,其中1 名是英國飛行員,另1名是外籍飛行員。在眾多的飛行員中,每一名外籍飛行員都可以與其他若干名英國飛行員很好地配合。如何選擇配對飛行的飛行員才能使一次派出最多的飛機。對於給定的外籍飛行員與英國飛行員的配合情況,試設計一個演算法找出最佳飛行員配對方案,使皇家空軍一次能派出最多的飛機。

對於給定的外籍飛行員與英國飛行員的配合情況,程式設計找出一個最佳飛行員配對方案,使皇家空軍一次能派出最多的飛機。

輸入輸出格式

輸入格式:

第 1 行有 2 個正整數 m 和 n。n 是皇家空軍的飛行員總數(n<100);m 是外籍飛行員數(m<=n)。外籍飛行員編號為 1~m;英國飛行員編號為 m+1~n。

接下來每行有 2 個正整數 i 和 j,表示外籍飛行員 i 可以和英國飛行員 j 配合。最後以 2個-1 結束。

輸出格式:

第 1 行是最佳飛行員配對方案一次能派出的最多的飛機數 M。接下來 M 行是最佳飛行員配對方案。每行有 2個正整數 i 和 j,表示在最佳飛行員配對方案中,飛行員 i 和飛行員 j 配對。如果所求的最佳飛行員配對方案不存在,則輸出‘No Solution!’。

題解:

本質上是二分圖最大匹配,直接建圖就好。

#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>

const int maxn=5000;
namespace Dinic {
    struct Edge{
        int u,v,cap,flow;
        Edge(int u,int v,int cap,int flow):u(u),v(v),cap(cap),flow(flow){
        }
    };
    std::vector<Edge> edges;
    std::vector<int> G[maxn];
    int n,s,t;
    int d[maxn];
    bool vis[maxn];
    int cur[maxn];
    void addEdge(int u,int v,int cap) {
        edges.push_back(Edge(u,v,cap,0));
        edges.push_back(Edge(v,u,0,0));
        int m=edges.size();
        G[u].push_back(m-2);
        G[v].push_back(m-1);
    }
    bool BFS() {
        std::queue<int> Q;
        memset(vis,0,sizeof(vis));
        memset(d,0,sizeof(d));
        Q.push(s);
        d[s]=0;
        vis[s]=1;
        while(!Q.empty()) {
            int u=Q.front();
            Q.pop();
            for(register int i=0;i<G[u].size();++i) {
                Edge& e=edges[G[u][i]];
                if(!vis[e.v]&&e.cap>e.flow) {
                    d[e.v]=d[u]+1;
                    Q.push(e.v);
                    vis[e.v]=1;
                }
            }
        }
        return vis[t];
    }
    
    int DFS(int u,int a) {
        if(u==t||a==0) return a;
        int flow=0,f;
        for(register int &i=cur[u];i<G[u].size();++i) {
            Edge& e=edges[G[u][i]];
            if(d[e.v]==d[u]+1&&(f=DFS(e.v,std::min(a,e.cap-e.flow)))>0) {
                e.flow+=f;
                a-=f;
                edges[G[u][i]^1].flow-=f;
                flow+=f;
                if(a==0) break;
            }   
        }
        return flow;
    }
    int maxFlow(int s,int t) {
        Dinic::s=s;
        Dinic::t=t;
        int ans=0;
        while(BFS()) {
            memset(cur,0,sizeof(cur));
            ans+=DFS(s,t);
        }
        return ans;
    }
}

int n,m,a,b;
int main() {
//  freopen("test.txt","r",stdin);
    scanf("%d%d",&m,&n);
    scanf("%d%d",&a,&b);
    while(a!=-1&&b!=-1) {
        Dinic::addEdge(a,b,1);
        scanf("%d%d",&a,&b);
    }
    for(register int i=1;i<=m;i++) {
        Dinic::addEdge(0,i,1);
    }
    for(register int i=1;i<=n;i++) {
        Dinic::addEdge(i+m,n+m+1,1);
    }
    int mx=Dinic::maxFlow(0,n+m+1);
    if(mx==0) {
        puts("No Solution!");
        return 0;
    }
    printf("%d\n",mx);
    for(register int i=1;i<=m;i++) {
        for(register int j=0;j<Dinic::G[i].size();++j) {
            Dinic::Edge& e = Dinic::edges[Dinic::G[i][j]];
            if(e.flow==1){
            printf("%d %d\n",i,e.v);
            break;
            }
        }
    }
    return 0;
}

軟體補丁問題

題目背景

none!

題目描述

T 公司發現其研製的一個軟體中有 n 個錯誤,隨即為該軟體發放了一批共 m 個補丁程式。每一個補丁程式都有其特定的適用環境,某個補丁只有在軟體中包含某些錯誤而同時又不包含另一些錯誤時才可以使用。一個補丁在排除某些錯誤的同時,往往會加入另一些錯誤。

換句話說,對於每一個補丁 i,都有 2 個與之相應的錯誤集合 B1[i]和 B2[i],使得僅當軟體包含 B1[i]中的所有錯誤,而不包含 B2[i]中的任何錯誤時,才可以使用補丁 i。補丁 i 將修復軟體中的某些錯誤 F1[i],而同時加入另一些錯誤 F2[i]。另外,每個補丁都耗費一定的時間。

試設計一個演算法,利用 T 公司提供的 m 個補丁程式將原軟體修復成一個沒有錯誤的軟體,並使修復後的軟體耗時最少。對於給定的 n 個錯誤和 m 個補丁程式,找到總耗時最少的軟體修復方案。

輸入輸出格式

輸入格式:

第 1 行有 2 個正整數 n 和 m,n 表示錯誤總數,m表示補丁總數,1<=n<=20, 1<=m<=100。

接下來 m 行給出了 m 個補丁的資訊。每行包括一個正整數,表示執行補丁程式 i 所需時間,以及 2 個長度為 n 的字串,中間用一個空格符隔開。

第 1 個字串中,如果第 k 個字元 bk 為“+”,則表示第 k 個錯誤屬於 B1[i],若為“-”,則表示第 k 個錯誤屬於 B21[i],若為“0”,則第 k 個錯誤既不屬於 B1[i]也不屬於 B2[i],即軟體中是否包含第 k 個錯誤並不影響補丁 i 的可用性。

第 2 個字串中,如果第 k 個字元 bk為“-”,則表示第 k 個錯誤屬於 F1[i],若為“+”,則表示第 k 個錯誤屬於 F2[i],若為“0”,則第 k 個錯誤既不屬於 F1[i]也不屬於 F2[i],即軟體中是否包含第 k 個錯誤不會因使用補丁i 而改變。

輸出格式:

程式執行結束時,將總耗時數輸出。如果問題無解,則輸出 0。

題解

沒想到網路流做法,可以直接跑最短路。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<string>
#include<ctime>
#define LL long long
using namespace std;
const int N=21,M=101,inf=1e9;
int n,m,val[M],b1[M],b2[M],f1[M],f2[M],dis[1<<N],q[1<<(N+1)],h,t;
bool inq[1<<N];
char s[N];
int read() {int d=0,f=1; char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();} while (c>='0'&&c<='9') d=(d<<3)+(d<<1)+c-48,c=getchar(); return d*f;}
void init()
{
    n=read(); m=read();
    for (int i=1;i<=m;i++)
    {
        val[i]=read();
        scanf("%s",&s);
        for (int j=0;j<n;j++)
            if (s[j]=='+') b1[i]|=(1<<j);
            else if (s[j]=='-') b2[i]|=(1<<j);
        scanf("%s",&s);
        for (int j=0;j<n;j++)
            if (s[j]=='+') f2[i]|=(1<<j);
            else if (s[j]=='-') f1[i]|=(1<<j);
    }
}
void spfa()
{
    int ljj=(1<<n)-1;
    for (int i=0;i<ljj;i++) dis[i]=inf;
    h=1; t=1;
    q[1]=ljj; dis[ljj]=0;
    while (h<=t)
    {
        int x=q[h++]; inq[x]=0;
        for (int i=1;i<=m;i++)
            if (!(x&b2[i])&&(x|b1[i])==x)
            {
                int y=x&(~f1[i])|f2[i];
                if (dis[x]+val[i]<dis[y])
                {
                    dis[y]=dis[x]+val[i];
                    if (!inq[y]) q[++t]=y,inq[y]=1;
                }
            }
    }
}
int main()
{
    init();
    spfa();
    if (dis[0]==inf) printf("0");
    else printf("%d",dis[0]);
    return 0;
}