|
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
没懂。。
其中输入规格有三种情况: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; }
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; }