ID
Origin
Title
  34 / 81 Problem A POJ 3436 ACM Computer Factory
  92 / 195 Problem B POJ 3281 Dining
  55 / 148 Problem C POJ 1087 A Plug for UNIX
  59 / 111 Problem D POJ 2195 Going Home
  44 / 132 Problem E POJ 2516 Minimum Cost
  35 / 72 Problem F POJ 1459 Power Network
  40 / 217 Problem G HDU 4280 Island Transport
  42 / 133 Problem H HDU 4292 Food
  34 / 90 Problem I HDU 4289 Control
  28 / 90 Problem J UVA 10480 Sabotage
  19 / 37 Problem K HDU 2732 Leapin' Lizards
  13 / 136 Problem L HDU 3338 Kakuro Extension
  36 / 358 Problem M HDU 3605 Escape
  17 / 58 Problem N HDU 3081 Marriage Match II
  15 / 61 Problem O HDU 3416 Marriage Match IV
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


34 / 81 Problem A POJ 3436 ACM Computer Factory

没懂。。

此题是最大流问题。题目意思比较难懂。
看得比较纠结。
就是说有N台组装电脑的机器。电脑的组成部分共有P部分。
每台机器有P个输入输出规格。还有一个容量Q;

其中输入规格有三种情况:0,1,2

0:该部分不能存在

1:该部分必须保留

2:该部分可有可无

输出规格有2种情况:0,1

0:该部分不存在

1:该部分存在

要求的是生产电脑最大的台数,就是求网络中的最大流。

这相当于是生产线。需要自己去建图。

但是考虑到每台机器都有容量,所以把一台机器分成两个点,中间建一条容量的边。

同时如果一台机器的输出符合另一台机器的输入,则建一条容量无穷大的边。

 

同时要增加源点和汇点。输入没有1的连接源点,输出全部是1的连接汇点。


92 / 195 Problem B POJ 3281 Dining

本题能够想到用最大流做,那真的是太绝了。建模的方法很妙!
题意就是有N头牛,F个食物,D个饮料。
N头牛每头牛有一定的喜好,只喜欢几个食物和饮料。
每个食物和饮料只能给一头牛。一头牛只能得到一个食物和饮料。
而且一头牛必须同时获得一个食物和一个饮料才能满足。问至多有多少头牛可以获得满足。
最初相当的是二分匹配。但是明显不行,因为要分配两个东西,两个东西还要同时满足。
最大流建图是把食物和饮料放在两端。一头牛拆分成两个点,两点之间的容量为1.喜欢的食物和饮料跟牛建条边,容量为1.
加个源点和汇点。源点与食物、饮料和汇点的边容量都是1,表示每种食物和饮料只有一个。
这样话完全是最大流问题了,。

/*
POJ 3281 最大流
//源点-->food-->牛(左)-->牛(右)-->drink-->汇点
//精髓就在这里,牛拆点,确保一头牛就选一套food和drink的搭配

*/

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std;

//****************************************************
//最大流模板
//初始化:g[][],start,end
//******************************************************
const int MAXN=500;
const int INF=0x3fffffff;
int g[MAXN][MAXN];//存边的容量,没有边的初始化为0
int path[MAXN],flow[MAXN],start,end;
int n;//点的个数,编号0-n.n包括了源点和汇点。

queue<int>q;
int bfs()
{
    int i,t;
    while(!q.empty())q.pop();//把清空队列
    memset(path,-1,sizeof(path));//每次搜索前都把路径初始化成-1
    path[start]=0;
    flow[start]=INF;//源点可以有无穷的流流进
    q.push(start);
    while(!q.empty())
    {
        t=q.front();
        q.pop();
        if(t==end)break;
        //枚举所有的点,如果点的编号起始点有变化可以改这里
        for(i=0;i<=n;i++)
        {
            if(i!=start&&path[i]==-1&&g[t][i])
            {
                flow[i]=flow[t]<g[t][i]?flow[t]:g[t][i];
                q.push(i);
                path[i]=t;
            }
        }
    }
    if(path[end]==-1)return -1;//即找不到汇点上去了。找不到增广路径了
    return flow[end];
}
int Edmonds_Karp()
{
    int max_flow=0;
    int step,now,pre;
    while((step=bfs())!=-1)
    {
        max_flow+=step;
        now=end;
        while(now!=start)
        {
            pre=path[now];
            g[pre][now]-=step;
            g[now][pre]+=step;
            now=pre;
        }
    }
    return max_flow;
}
int main()
{
    int N,F,D;
    while(scanf("%d%d%d",&N,&F,&D)!=EOF)
    {
        memset(g,0,sizeof(g));
        n=F+D+2*N+1;
        start=0;
        end=n;
        for(int i=1;i<=F;i++)g[0][i]=1;
        for(int i=F+2*N+1;i<=F+2*N+D;i++)g[i][n]=1;
        for(int i=1;i<=N;i++)g[F+2*i-1][F+2*i]=1;
        int k1,k2;
        int u;
        for(int i=1;i<=N;i++)
        {
            scanf("%d%d",&k1,&k2);
            while(k1--)
            {
                scanf("%d",&u);
                g[u][F+2*i-1]=1;
            }
            while(k2--)
            {
                scanf("%d",&u);
                g[F+2*i][F+2*N+u]=1;
            }
        }
        printf("%d\n",Edmonds_Karp());
    }
    return 0;
}
View Code

 

55 / 148 Problem C POJ 1087 A Plug for UNIX

题意:在一个房间里有N种插座和使用M种插座的电器,,这M种插座有些在现有的插座中不包含,不过可以通过适配器来转化,有K种类型的适配器来转化,让你求最少会有多少电器没法使用插座。

思路:最大二分匹配。即求出最多有多少电器有相应的插座,然后用M减去就是所求,不过建图有点难想,我也是看了别人的解题报告才明白的。将电器和相应的插座类型连接起来,将能相互转化的插座连接起来,然后将不能直接使用N种插座而通过适配器的转换就能用的电器和插座连起来,然后就是求M种电器和N种插座的最大匹配了。呃,其实看到很多博客都是用最大流做的,原本这题也是在最大流的练习中找到的,但是我发现最大匹配更好理解,而且二分匹配加上源点和汇点就能转化成最大流。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#include <map>
#include <string>
#define  N 505
using namespace std ;

map<string , int>p ;
int mp[N][N] ;
int n , m , k , nx ;
int f[N] , d[N] ;

int dfs( int x )
{
    int i ;

    for ( i = m + 1; i <= m + nx ; i++ )
    {
        if ( !f[i] && mp[x][i] )
        {
            f[i] = 1 ;
            if ( !d[i] || dfs( d[i] ))
            {
                d[i] = x ;
                return 1 ;
            }
        }
    }
    return 0 ;
}
//二分匹配模板
int floyd( )
{
    int i , sum ;
    sum = 0 ;
    memset( d , 0 , sizeof ( d ));
    for ( i = 1 ; i <= m ; i++ )
    {
        memset( f , 0 , sizeof ( f ));
        if ( dfs( i ))
        sum++;
    }
    return sum ;
}

int main()
{
    int i , j , t ;
    string str1 , str2 ;

    //freopen("input.txt" , "r" , stdin );
    //输入N种插座
    scanf ( "%d" , &n ) ;
    p.clear();
    nx = n ;
    for ( i = 1 ; i <= n ; i++ )
    {
        cin>>str1 ;
        p[str1] = i ;
    }
    
    //输入M种电器
    scanf ( "%d" , &m );
    for ( i = 1 ; i <= m ; i++ )
    {
        cin>>str1>>str2 ;
        if ( p[str2] != 0 )
        {
            int x = p[str2] ;
            mp[i][x+m] = 1 ;
        }
        else
        {
            n++ ;
            p[str2] = n ;
            mp[i][n+m] = 1 ;
        }
    }
    
    //输入K种转化关系
    scanf ( "%d" , &k );
    for ( i = 1 ; i <= k ; i++ )
    {
        cin>>str1>>str2 ;
        if ( p[str1] == 0 )
        {
            n++ ;
            p[str1] = n ;
        }
        if ( p[str2] == 0 )
        {
            n++ ;
            p[str2] = n ;
        }
        mp[p[str1]+m][p[str2]+m] = 1 ;
    }
    
    //将通过适配器可以使用原来N种插座的电器连起来。
    for ( i = 1 ; i <= m + n ; i++ )
    for ( j = 1 ; j <= m + n ; j++ )
    for ( t = 1 ; t <= m + n ; t++ )
    if ( mp[j][i] && mp[i][t] && !mp[j][t] )
    mp[j][t] = 1 ;
    
    int flow = floyd( );
    printf ( "%d\n" , m - flow ) ;
    return 0;
}
View Code

相关文章: