zoukankan      html  css  js  c++  java
  • 网络流24题 (一)

        

        网络流基本知识就不在这里阐述了

        算法实现题 8-1 飞行员配对方案问题



      问题描述:
          第二次世界大战时期,英国皇家空军从沦陷国征募了大量外籍飞行员。由皇家空军派出
        的每一架飞机都需要配备在航行技能和语言上能互相配合的 2 名飞行员, 其中 1 名是英国飞
        行员,另 1 名是外籍飞行员。在众多的飞行员中,每一名外籍飞行员都可以与其他若干名英
        国飞行员很好地配合。如何选择配对飞行的飞行员才能使一次派出最多的飞机。对于给定的
        外籍飞行员与英国飞行员的配合情况,试设计一个算法找出最佳飞行员配对方案,使皇家空
        军一次能派出最多的飞机。
      编程任务:
          对于给定的外籍飞行员与英国飞行员的配合情况,编程找出一个最佳飞行员配对方案,
        使皇家空军一次能派出最多的飞机。
      数据输入:
          由文件 input.txt 提供输入数据。文件第 1 行有 2 个正整数 m 和 n。n 是皇家空军的飞行
        员总数(n<100);m 是外籍飞行员数。外籍飞行员编号为 1~m;英国飞行员编号为 m+1~n。
        接下来每行有 2 个正整数 i 和 j,表示外籍飞行员 i 可以和英国飞行员 j 配合。文件最后以 2
        个-1 结束。
      结果输出:
          程序运行结束时,将最佳飞行员配对方案输出到文件 output.txt 中。第 1 行是最佳飞行
        员配对方案一次能派出的最多的飞机数 M。接下来 M 行是最佳飞行员配对方案。每行有 2
        个正整数 i 和 j,表示在最佳飞行员配对方案中,飞行员 i 和飞行员 j 配对。
        如果所求的最佳飞行员配对方案不存在,则输出‘No Solution!’ 。


      我们先画个图~

      我们发现这题就是求二分图的最大匹配?

      好吧,你有两个选择 

      1.敲KM

      2.外籍飞行员放在x集合里面,英国王牌飞行员放在y集合里面,设立源点S,汇点T,S向每个x集合里面的点连一条容量为1的边(限制每个点只能连出一条边),y集合里面每个点连一条容量为1的点到T,每个配对连容量为1的边,跑最大流。

      网络流第一个建模。。还体验了一把输出方案的可怕之处。(从此立下没有SPJ || 唯一解 不打方案的flag!)

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #include<cmath>
     5 #include<queue>
     6 
     7 #define maxn 102
     8 
     9 using namespace std;
    10 
    11 int dis[maxn],dp[maxn][maxn],m;
    12 
    13 bool DFS()
    14 {
    15     memset(dis,-1,sizeof(dis));
    16     dis[0]=1;
    17     queue<int>q;
    18     q.push(0);
    19     while(!q.empty())
    20     {
    21         int u=q.front();
    22         if(u==m+1)break;
    23         q.pop();
    24         for(int i=0;i<=m+1;i++)if(dis[i]==-1&&dp[u][i]>0){
    25             dis[i]=dis[u]+1;
    26             q.push(i);
    27         }
    28     }
    29     if(dis[m]==-1)return 0;
    30     else return 1;
    31 }
    32 
    33 int find(int poi,int low)
    34 {
    35     int a;
    36     if(poi==m+1)return low;
    37     for(int i=0;i<=m+1;i++)if(dp[poi][i]>0&&dis[i]==dis[poi]+1){
    38         a=find(i,min(low,dp[poi][i]));
    39         if(a){
    40             dp[poi][i]-=a;
    41             dp[i][poi]+=a;
    42             return a;
    43         }
    44     }
    45     return 0;
    46 }
    47 
    48 int main()
    49 {
    50     int a,n,x,y,ans=0;
    51     scanf("%d%d%d%d",&n,&m,&x,&y);
    52     while(x!=-1&&y!=-1)
    53     {
    54         dp[x][y]=1;
    55         scanf("%d%d",&x,&y);
    56     }
    57     for(int i=1;i<=n;i++)
    58         dp[0][i]=1;
    59     for(int i=n+1;i<=m;i++)
    60         dp[i][m+1]=1;
    61     while(DFS())
    62     {    
    63         a=find(0,99999999);
    64         while(a)
    65         {
    66             ans+=a;
    67             a=find(0,99999999);
    68         }
    69     }
    70     printf("%d",ans);
    71     return 0;
    72 }
    View Code

        算法实现题 8-2 太空飞行计划问题


      问题描述:
           W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业
        性实验而获取利润。现已确定了一个可供选择的实验集合 E={E1,E2,…,Em},和进行这
        些实验需要使用的全部仪器的集合 I={I1, I2,…In}。 实验 Ej需要用到的仪器是 I 的子集 RjÍI。
        配置仪器 Ik的费用为 ck美元。实验 Ej的赞助商已同意为该实验结果支付 pj美元。W 教授的
        任务是找出一个有效算法, 确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才
        能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部
        费用的差额。
      编程任务:
          对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。
      数据输入:
          由文件 input.txt 提供输入数据。文件第 1 行有 2 个正整数 m 和 n。m 是实验数,n 是仪
        器数。接下来的 m 行,每行是一个实验的有关数据。第一个数赞助商同意支付该实验的费
        用;接着是该实验需要用到的若干仪器的编号。最后一行的 n 个数是配置每个仪器的费用。
      结果输出:
          程序运行结束时,将最佳实验方案输出到文件 output.txt 中。第 1 行是实验编号;第 2
        行是仪器编号;最后一行是净收益。


        一道最小割的好题!求最小割==求最大流。。或者用某专门求最小割的算法

        关于一些证明:http://www.cnblogs.com/wuyiqi/archive/2012/03/12/2391960.html

        我不信邪再输了一次方案。下面是代码。结果呵呵了。flag继续立起来!

      1 #include<cstdio>
      2 #include<cstring>
      3 #include<cmath>
      4 #include<algorithm>
      5 #include<vector>
      6 #include<queue>
      7 
      8 #define maxn 121
      9 
     10 using namespace std;
     11 
     12 vector <int> graph[maxn];
     13 vector <int> cgraph[maxn];
     14 vector <int> opp[maxn];
     15 
     16 int n,m,dis[maxn];
     17 
     18 bool g[maxn];
     19 
     20 bool DFS()
     21 {
     22     memset(dis,-1,sizeof(dis));
     23     queue<int>q;
     24     dis[0]=1;
     25     for(int i=0;i<graph[0].size();i++)if(cgraph[0][i]>0){
     26         int v=graph[0][i];
     27         dis[v]=2;
     28         q.push(v);
     29     }
     30     while(!q.empty())
     31     {
     32         int u=q.front();
     33         q.pop();
     34         for(int i=0;i<graph[u].size();i++)
     35             if(cgraph[u][i]>0&&dis[graph[u][i]]==-1)
     36             {
     37                 dis[graph[u][i]]=dis[u]+1;
     38                 q.push(graph[u][i]);
     39                 if(graph[u][i]==n+1+m)return 1;
     40             }
     41     }
     42     return 0;
     43 }
     44 
     45 int find(int poi,int low)
     46 {
     47     if(poi==n+m+1)return low;
     48     for(int i=0;i<graph[poi].size();i++)if(cgraph[poi][i]>0&&dis[graph[poi][i]]==dis[poi]+1){
     49         int a=find(graph[poi][i],min(low,cgraph[poi][i]));
     50         if(a)
     51         {
     52             cgraph[poi][i]-=a;
     53             int v=opp[poi][i];
     54             cgraph[graph[poi][i]][v]+=a;
     55             return a;
     56         }
     57     }
     58     return 0;
     59 }
     60 
     61 void addedge(int a,int b,int c)
     62 {
     63     graph[a].push_back(b);
     64     cgraph[a].push_back(c);
     65     opp[b].push_back(graph[a].size()-1);
     66     graph[b].push_back(a);
     67     cgraph[b].push_back(0);
     68     opp[a].push_back(graph[b].size()-1);
     69 }
     70 
     71 void luangao(int poi)
     72 {
     73     for(int i=0;i<graph[poi].size();i++)
     74     {
     75         int flag=0;
     76         if(cgraph[poi][i]>0)continue;
     77         for(int j=0;j<graph[graph[poi][i]].size();j++)
     78             for(int k=0;k<graph[graph[graph[poi][i]][j]].size();k++)
     79                 if(graph[graph[graph[poi][i]][j]][k]==m+n+1&&cgraph[graph[graph[poi][i]][j]][k]!=0)flag=1;
     80         if(flag==0){g[graph[poi][i]]=1;for(int j=0;j<graph[graph[poi][i]].size();j++)g[graph[graph[poi][i]][j]]=1;}
     81     }
     82 }
     83 
     84 void dfs(int poi)
     85 {
     86     for(int i=0;i<graph[poi].size();i++)if(cgraph[poi][i]>0&&!g[graph[poi][i]]){
     87         g[graph[poi][i]]=1;
     88         dfs(graph[poi][i]);
     89     }
     90 }
     91 
     92 int main()
     93 {
     94     freopen("shut.in","r",stdin);
     95        freopen("shut.out","w",stdout);
     96     int x=1,ans=0,sum=0;
     97     char s;
     98     scanf("%d%d",&n,&m);
     99     for(int i=1;i<=n;i++)
    100     {
    101         scanf("%d ",&x);ans+=x;
    102         addedge(0,i,x);
    103         int ss=0;
    104         while(1)
    105         {
    106             scanf("%c",&s);
    107             if(s=='
    '){
    108                 if(ss)addedge(i,ss+n,99999999);
    109                 break;
    110             }
    111             else if(s==' ')addedge(i,n+ss,99999999),ss=0;
    112             else ss=ss*10+s-'0';
    113         }
    114     }
    115     for(int i=1;i<=m;i++)
    116         scanf("%d",&x),addedge(i+n,n+m+1,x);
    117      int a;
    118     while(DFS())
    119     {
    120          while(a=find(0,99999999))
    121             sum+=a;
    122     }
    123     dfs(0);
    124 //    luangao(0);
    125     for(int i=1;i<=n;i++)
    126         if(g[i]==1)printf("%d ",i);
    127     printf("
    ");
    128     for(int i=1;i<=m;i++)
    129         if(g[i+n]==1)printf("%d ",i);
    130     printf("
    %d",ans-sum);
    131     return 0;
    132 }
    View Code 

        算法实现题 8-3 最小路径覆盖问题


        问题描述:
           给定有向图 G=(V,E)。设 P 是 G 的一个简单路(顶点不相交)的集合。如果 V 中每个
        顶点恰好在 P 的一条路上,则称 P 是 G 的一个路径覆盖。P 中路径可以从 V 的任何一个顶
        点开始,长度也是任意的,特别地,可以为 0。G 的最小路径覆盖是 G 的所含路径条数最少
        的路径覆盖。
           设计一个有效算法求一个有向无环图 G 的最小路径覆盖。

        编程任务:
          对于给定的给定有向无环图 G,编程找出 G 的一个最小路径覆盖.
       数据输入:
          由文件 input.txt 提供输入数据。文件第 1 行有 2 个正整数 n 和 m。n 是给定有向无环图
        G 的顶点数, m 是 G 的边数。接下来的 m 行,每行有 2 个正整数 i 和 j,表示一条有向边(i,j)
        结果输出:
          程序运行结束时,将最小路径覆盖输出到文件 output.txt 中。从第 1 行开始,每行输出
        一条路径。文件的最后一行是最少路径数。


        首先我们知道选择的边数越多,路径就越少。

        所以我们只需把每个点拆成两个,放入x,y集合里面,然后按8-1的方法连边,跑最大流,用点数-最大流即可。

        这题又要输方案- -但是我拒绝!

        View Code


        算法实现题 8-4 最小路径覆盖问题



      问题描述:
          假设有 n 根柱子,现要按下述规则在这 n 根柱子中依次放入编号为 1,2,3,。。的球。
        (1)每次只能在某根柱子的最上面放球。
        (2)在同一根柱子中,任何 2 个相邻球的编号之和为完全平方数。
        试设计一个算法,计算出在 n 根柱子上最多能放多少个球。例如,在 4 根柱子上最多可
        放 11 个球。
      编程任务:
          对于给定的 n,计算在 n 根柱子上最多能放多少个球。
      数据输入:
          由文件 input.txt 提供输入数据。文件第 1 行有 1 个正整数 n,表示柱子数。
      结果输出:
          程序运行结束时,将 n 根柱子上最多能放的球数以及相应的放置方案输出到文件
        output.txt 中。文件的第一行是球数。接下来的 n 行,每行是一根柱子上的球的编号。


        首先直接出答案是不行的啦。。(听说有通项公式 - -)

        于是我们枚举答案,然后把能放在相邻的连一条边做最小路径覆盖然后看ans,ans大于答案就输出,否则继续,另外每次不需要额外建图,直接残量网络上加边继续跑就是的啦。。

        拒绝方案!

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cmath>
     4 #include<algorithm>
     5 #include<vector>
     6 #include<queue>
     7 
     8 #define maxn 3202
     9 
    10 using namespace std;
    11 
    12 vector <int> graph[maxn];
    13 vector <int> fgraph[maxn];
    14 vector <int> cgraph[maxn];
    15 
    16 int pf[61],dis[3202];
    17 
    18 void addedge(int a,int b,int c)
    19 {
    20     graph[a].push_back(b);
    21     cgraph[a].push_back(c);
    22     fgraph[b].push_back(graph[a].size()-1);
    23     graph[b].push_back(a);
    24     cgraph[b].push_back(0);
    25     fgraph[a].push_back(graph[b].size()-1);
    26 }
    27 
    28 void prework(int k)
    29 {
    30     addedge(0,k,1);
    31     addedge(k+1600,3201,1);
    32     for(int i=sqrt(k)+1;pf[i]-k<k;i++)
    33         addedge(pf[i]-k,k+1600,99999999);
    34 }
    35 
    36 bool DFS()
    37 {
    38     memset(dis,-1,sizeof(dis));
    39     dis[0]=1;
    40     queue<int>q;
    41     for(int i=0;i<graph[0].size();i++)if(cgraph[0][i]>0&&dis[graph[0][i]]==-1){
    42         dis[graph[0][i]]=2;
    43         q.push(graph[0][i]);
    44     }
    45     while(!q.empty())
    46     {
    47         int u=q.front();
    48         q.pop();
    49         for(int i=0;i<graph[u].size();i++)
    50         {
    51     //        printf("%d %d",graph[u][i],cgraph[u][i]);
    52             if(cgraph[u][i]>0&&dis[graph[u][i]]==-1){
    53                 dis[graph[u][i]]=dis[u]+1;
    54                 q.push(graph[u][i]);
    55                 if(graph[u][i]==3201)return 1;
    56             }
    57         }
    58     }
    59     return 0;
    60 }
    61 
    62 int find(int poi,int low)
    63 {
    64     if(poi==maxn-1)return low;
    65     for(int i=0;i<graph[poi].size();i++)if(cgraph[poi][i]>0&&dis[graph[poi][i]]==dis[poi]+1){
    66         int a=find(graph[poi][i],min(low,cgraph[poi][i]));
    67         if(a){
    68             cgraph[poi][i]-=a;
    69             cgraph[graph[poi][i]][fgraph[poi][i]]+=a;
    70             return a;
    71         }
    72     }
    73     return 0;
    74 }
    75 
    76 int main()
    77 {
    78     freopen("ball.in","r",stdin);
    79     freopen("ball.out","w",stdout);
    80     int n;
    81     for(int i=1;i<=60;i++)pf[i]=i*i;
    82     scanf("%d",&n);
    83     int sum=0,a;
    84     for(int i=1;;i++)
    85     {
    86         prework(i);
    87         while(DFS()){while(a=find(0,99999999))sum+=a;}
    88         if(i-sum>n){printf("%d",i-1);return 0;}
    89     }
    90 }
    View Code 

        算法实现题 8-5 圆桌问题


        问题描述:
          假设有来自 n 个不同单位的代表参加一次国际会议。每个单位的代表数分别为
        ri,i , = 1,2,.., 。会议餐厅共有 m 张餐桌,每张餐桌可容纳ci (i = 1,2,.., m) 个代表就餐。
        为了使代表们充分交流, 希望从同一个单位来的代表不在同一个餐桌就餐。 试设计一个算法,
        给出满足要求的代表就餐方案。
        编程任务:
          对于给定的代表数和餐桌数以及餐桌容量,编程计算满足要求的代表就餐方案。
       数据输入:
          由文件 input.txt 提供输入数据。文件第 1 行有 2 个正整数 m 和 n,m 表示单位数,n 表
        示餐桌数,1<=m<=150, 1<=n<=270。文件第 2 行有 m 个正整数,分别表示每个单位的代表
        数。文件第 3 行有 n 个正整数,分别表示每个餐桌的容量。
          结果输出:
          程序运行结束时,将代表就餐方案输出到文件 output.txt 中。如果问题有解,在文件第
        1 行输出 1,否则输出 0。接下来的 m 行给出每个单位代表的就餐桌号。如果有多个满足要
        求的方案,只要输出 1 个方案。


        一眼看出桌子在左边,单位在右边,源点连桌子容量,汇点连代表数,每个桌子连容量为1的边,跑最大流。

        View Code

        看起来方案很好搞,结果。。呵呵。。


    ·    算法实现题 8-6 圆桌问题


        问题描述:
          给定正整数序列x1,.. xn 
        (1)计算其最长递增子序列的长度 s。
        (2)计算从给定的序列中最多可取出多少个长度为 s 的递增子序列。
        (3)如果允许在取出的序列中多次使用 x1和 xn,则从给定序列中最多可取出多少个长
        度为 s 的递增子序列。
         编程任务:
          设计有效算法完成(1)(2)(3)提出的计算任务。
        数据输入:
          由文件 input.txt 提供输入数据。文件第 1 行有 1 个正整数 n,表示给定序列的长度。接
        下来的 1 行有 n 个正整数.
          结果输出:
          程序运行结束时,将任务(1)(2)(3)的解答输出到文件 output.txt 中。第 1 行是最长
        递增子序列的长度 s。第 2 行是可取出的长度为 s 的递增子序列个数。第 3 行是允许在取出
        的序列中多次使用 x1和 xn时可取出的长度为 s 的递增子序列个数。


        第一问先DP艹过。。

        然后我们就可以开始建模啦!

        1、把序列每位i拆成两个点<i.a>和<i.b>,从<i.a>到<i.b>连接一条容量为1的有向边。
        2、建立附加源S和汇T,如果序列第i位有F[i]=K,从S到<i.a>连接一条容量为1的有向边。
        3、如果F[i]=1,从<i.b>到T连接一条容量为1的有向边。
        4、如果j>i且A[i] < A[j]且F[j]+1=F[i],从<i.b>到<j.a>连接一条容量为1的有向边。

        第二问跑最大流,第三问把边(<1.a>,<1.b>)(<N.a>,<N.b>)(S,<1.a>)(<N.b>,T)这四条边的容量修改为无穷大,再求一次网络最大流,就是第三问结果。  

      1     #include<cstdio>
      2     #include<cstring>
      3     #include<queue>
      4     #include<cmath>
      5     #include<algorithm>
      6     
      7     #define maxn 501
      8     
      9     using namespace std;
     10     
     11     int o[maxn],num[maxn],b=0,dis[maxn*2],n;
     12     
     13     vector<int>graph[maxn*2];
     14     vector<int>cgraph[maxn*2];
     15     vector<int>fgraph[maxn*2];
     16     
     17     void addedge(int a,int b,int c)
     18     {
     19         graph[a].push_back(b);
     20         cgraph[a].push_back(c);
     21         fgraph[b].push_back(graph[a].size()-1);
     22         graph[b].push_back(b);
     23         cgraph[b].push_back(0);
     24         fgraph[a].push_back(graph[b].size()-1);
     25     }
     26     
     27     void clear(int kk){
     28         vector <int> neww;
     29         vector <int> new1;
     30         vector <int> new2;
     31         swap(neww,graph[kk]);
     32         swap(new1,cgraph[kk]);
     33         swap(new2,fgraph[kk]);
     34     }
     35     
     36     bool DFS()
     37     {
     38         memset(dis,-1,sizeof(dis));
     39         dis[0]=1;queue<int>q;
     40         q.push(0);
     41         while(!q.empty())
     42         {
     43             int u=q.front();
     44             q.pop();
     45             for(int i=0;i<graph[u].size();i++)if(cgraph[u][i]>0&&dis[graph[u][i]]==-1){
     46                 dis[graph[u][i]]=dis[u]+1;
     47                 q.push(graph[u][i]);
     48                 if(graph[u][i]==n*2+1)return true;
     49             }
     50         }
     51         return false;
     52     }
     53     
     54     int find(int poi,int low)
     55     {
     56         if(poi==2*n+1)return low;
     57         for(int i=0;i<graph[poi].size();i++)if(cgraph[poi][i]>0&&dis[graph[poi][i]]==dis[poi]+1){
     58             int a=find(graph[poi][i],min(cgraph[poi][i],low));
     59             if(a)
     60             {
     61                 cgraph[poi][i]-=a;
     62                 cgraph[graph[poi][i]][fgraph[poi][i]]+=a;
     63                 return a;
     64             }
     65         }
     66         return 0;
     67     }
     68     
     69     int main()
     70     {
     71         freopen("alis.in","r",stdin);
     72         freopen("alis.out","w",stdout);
     73         scanf("%d",&n);
     74         for(int i=1;i<=n;i++)
     75             scanf("%d",&o[i]);
     76         for(int i=1;i<=n;i++)
     77         {
     78             int kk=0; 
     79             for(int j=i-1;j>=1;j--)if(o[j]<=o[i]){
     80                 if(!kk)kk=j;
     81                 else if(num[kk]<num[j])kk=j;
     82             }
     83             if(!kk)num[i]=1;
     84             else num[i]=num[kk]+1;
     85         }
     86         int maxx=0;
     87         for(int i=1;i<=n;i++)
     88             maxx=max(maxx,num[i]);
     89         printf("%d
    ",maxx);
     90         for(int i=1;i<=n;i++)
     91         {
     92             addedge(i,i+n,1);
     93             if(num[i]==1)addedge(i+n,n*2+1,1);
     94             if(num[i]==maxx)addedge(0,i,1);
     95         }
     96         for(int i=1;i<=n;i++)
     97             for(int j=i+1;j<=n;j++)
     98                 if(o[j]>=o[i]&&num[j]==num[i]+1)addedge(j+n,i,1);
     99         int a,sum=0;
    100         while(DFS())while(a=find(0,99999999))sum+=a;
    101         printf("%d
    ",sum);
    102         for(int i=0;i<=2*n+1;i++)clear(i);
    103         for(int i=1;i<=n;i++)
    104         {
    105             if(i==n){
    106                 addedge(i,i+n,99999999);
    107                 if(num[n]==maxx)addedge(0,i,99999999);
    108             }
    109             if(i==1){
    110                 addedge(i,i+n,99999999);
    111                 addedge(i+n,n*2+1,99999999);
    112             }
    113             addedge(i,i+n,1);
    114             if(num[i]==1)addedge(i+n,n*2+1,1);
    115             if(num[i]==maxx)addedge(0,i,1);
    116         }
    117         for(int i=1;i<=n;i++)
    118             for(int j=i+1;j<=n;j++)
    119                 if(o[j]>=o[i]&&num[j]==num[i]+1)addedge(j+n,i,1);
    120         sum=0;
    121         while(DFS())while(a=find(0,99999999))sum+=a;
    122         printf("%d",sum);
    123         return 0;
    124     }
    View Code

        

  • 相关阅读:
    ConcurrentHashMap get方法保持同步的解释
    HashMap resize导致死循环
    ConcurrentHashMap原理详解
    单列索引和联合索引区别
    优先队列
    大根堆排序
    小根堆排序
    基础哈夫曼树-最简单的
    二叉查找树
    二叉查找树
  • 原文地址:https://www.cnblogs.com/tuigou/p/4687177.html
Copyright © 2011-2022 走看看