A题:
Description
AveryBoy喜欢玩LOL,但是他技术太菜,总是被别人喷“这么菜玩什么游戏,回家养猪去吧”。终于有一天,他被喷的受不了了,于是回家养猪。不过他家的养猪场在下雨天的时候总是被淹,所以他用读书学来的知识设计了一套排水系统。他还设计了一套装置,可以控制排水管道的水流流量。现在有n个排水管道,m个排水节点,问你从1到m的最大排水流量。
Input
Output
对于每种情况输出一个整数,表示从1到m的最大排水流量。
Sample Input
5 4
1 2 40
1 4 20
2 4 20
2 3 30
3 4 10
Sample Output
50
HINT
模板题,直接套用邻接表-ISAP即可
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; #define MAXN 100010 #define MAXM 400010 #define INF 0x3f3f3f3f struct Edge { int to,next,cap,flow; } edge[MAXM]; int tol; int head[MAXN]; int gap[MAXN],dep[MAXN],pre[MAXN],cur[MAXN]; void init() { tol=0; memset(head,-1,sizeof head); } void addedge(int u,int v,int w,int rw=0) { edge[tol].to=v; edge[tol].cap=w; edge[tol].next=head[u]; edge[tol].flow=0; head[u]=tol++; edge[tol].to=u; edge[tol].cap=rw; edge[tol].next=head[v]; edge[tol].flow=0; head[v]=tol++; } int sap(int start,int endd,int N) { memset(gap,0,sizeof gap); memset(dep,0,sizeof dep); memcpy(cur,head,sizeof head); int u=start; pre[u]=-1; gap[0]=N; int ans=0; while(dep[start]<N) { if(u==endd) { int Min=INF; for(int i=pre[u];i!=-1;i=pre[edge[i^1].to]) if(Min>edge[i].cap-edge[i].flow) Min=edge[i].cap-edge[i].flow; for(int i=pre[u];i!=-1;i=pre[edge[i^1].to]) { edge[i].flow+=Min; edge[i^1].flow-=Min; } u=start; ans+=Min; continue; } bool flag=false; int v; for(int i=cur[u];i!=-1;i=edge[i].next) { v=edge[i].to; if(edge[i].cap-edge[i].flow&&dep[v]+1==dep[u]) { flag=true; cur[u]=pre[v]=i; break; } } if(flag) { u=v; continue; } int Min=N; for(int i=head[u];i!=-1;i=edge[i].next) if(edge[i].cap-edge[i].flow&&dep[edge[i].to]<Min) { Min=dep[edge[i].to]; cur[u]=i; } gap[dep[u]]--; if(!gap[dep[u]]) return ans; dep[u]=Min+1; gap[dep[u]]++; if(u!=start) u=edge[pre[u]^1].to; } return ans; } int main() { int n,m; while(~scanf("%d%d",&n,&m)) { int i; init(); for(i=1;i<=n;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); } long long ans=sap(1,m,n); printf("%lld ",ans); } return 0; }
B题:
Description
Input
第一行输入包含一个整数T,表示测试用例的数量。
对于每个测试用例,第一行包含两个整数N和M,表示图中顶点和边的数量。(2 <= N <= 15,0 <= M <= 1000)
接下来的M行,每行包含三个整数X,Y和C,表示从X到Y有一个边,它的容量是C.(1 <= X,Y <= N,1 <= C <= 1000)
Output
Sample Input
2
3 2
1 2 1
2 3 1
3 3
1 2 1
2 3 1
1 3 1
Sample Output
Case 1: 1
Case 2: 2
HINT
模板,too
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; #define MAXN 100010 #define MAXM 400010 #define INF 0x3f3f3f3f struct Edge { int to,next,cap,flow; } edge[MAXM]; int tol; int head[MAXN]; int gap[MAXN],dep[MAXN],pre[MAXN],cur[MAXN]; void init() { tol=0; memset(head,-1,sizeof head); } void addedge(int u,int v,int w,int rw=0) { edge[tol].to=v; edge[tol].cap=w; edge[tol].next=head[u]; edge[tol].flow=0; head[u]=tol++; edge[tol].to=u; edge[tol].cap=rw; edge[tol].next=head[v]; edge[tol].flow=0; head[v]=tol++; } int sap(int start,int endd,int N) N是图中点的总个数 { memset(gap,0,sizeof gap); memset(dep,0,sizeof dep); memcpy(cur,head,sizeof head); int u=start; pre[u]=-1; gap[0]=N; int ans=0; while(dep[start]<N) { if(u==endd) { int Min=INF; for(int i=pre[u];i!=-1;i=pre[edge[i^1].to]) if(Min>edge[i].cap-edge[i].flow) Min=edge[i].cap-edge[i].flow; for(int i=pre[u];i!=-1;i=pre[edge[i^1].to]) { edge[i].flow+=Min; edge[i^1].flow-=Min; } u=start; ans+=Min; continue; } bool flag=false; int v; for(int i=cur[u];i!=-1;i=edge[i].next) { v=edge[i].to; if(edge[i].cap-edge[i].flow&&dep[v]+1==dep[u]) { flag=true; cur[u]=pre[v]=i; break; } } if(flag) { u=v; continue; } int Min=N; for(int i=head[u];i!=-1;i=edge[i].next) if(edge[i].cap-edge[i].flow&&dep[edge[i].to]<Min) { Min=dep[edge[i].to]; cur[u]=i; } gap[dep[u]]--; if(!gap[dep[u]]) return ans; dep[u]=Min+1; gap[dep[u]]++; if(u!=start) u=edge[pre[u]^1].to; } return ans; } int main() { int t; cin>>t; int d=1; while(t--) { int n,m,i; scanf("%d%d",&n,&m); init(); for(i=1;i<=m;i++) { int x,y,c; scanf("%d%d%d",&x,&y,&c); addedge(x,y,c); } printf("Case %d: %d ",d,sap(1,n,n)); d++; } return 0; }
C题:
Description
Input
有多组测试数据,每组测试数据第一行是3个正整数,n,f,d,表示老师个数,房子种数,车子种数。
第二行包含f个整数,其中第i个数表示第i种房子的个数。
第三行包含d个整数,其中第i个数表示第i种车子的个数。
之后n行,每行包含长度为f的字符串,其中第i行第j个字符表示第i个老师是否喜欢第j种房子,‘Y’表示喜欢,‘N’表示不喜欢。
之后n行,每行包含长度为d的字符串,其中第i行第j个字符表示第i个老师是否喜欢第j种车子,‘Y’表示喜欢,‘N’表示不喜欢。
Output
Sample Input
4 3 3
1 1 1
1 1 1
YYN
NYY
YNY
YNY
YNY
YYN
YYN
NNY
Sample Output
3
HINT
这题建图比较复杂
首先建立超级源点,与所有的房子连边,边的流量为房子的个数
然后建立超级汇点,与所有的车子连边,边的流量为车子的个数
然后关键的来了,把教师劈成两半,让教师自己与自己连边,边的流量为1,这样才能保证每个教师只能拥有一个房子和一辆车(老师拥有了一个房子之后那条路就流不了了)
然后把教师(1-n)和他喜欢的房子连边,边的流量为1(对于同一种房子老师当然只能拥有一个)
把教师(n+1-2*n)和他喜欢的车子连边,边的流量为1
注意标记清楚各个种类的点
超级源点-0
教师(1-n)(n+1-n*2)
房子(2*n+1-2*n+f)
车子(2*n+f+1,2*n+f+d)
超级汇点 2*n+f+d+1
总点数 2*n+f+d+2
还要注意边的流向,当然是单向边啊
邻接表模板
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; #include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; #define MAXN 100010 #define MAXM 400010 #define INF 0x3f3f3f3f struct Edge { int to,next,cap,flow; } edge[MAXM]; int tol; int head[MAXN]; int gap[MAXN],dep[MAXN],pre[MAXN],cur[MAXN]; void init() { tol=0; memset(head,-1,sizeof head); } void addedge(int u,int v,int w,int rw=0) { edge[tol].to=v; edge[tol].cap=w; edge[tol].next=head[u]; edge[tol].flow=0; head[u]=tol++; edge[tol].to=u; edge[tol].cap=rw; edge[tol].next=head[v]; edge[tol].flow=0; head[v]=tol++; } int sap(int start,int endd,int N) { memset(gap,0,sizeof gap); memset(dep,0,sizeof dep); memcpy(cur,head,sizeof head); int u=start; pre[u]=-1; gap[0]=N; int ans=0; while(dep[start]<N) { if(u==endd) { int Min=INF; for(int i=pre[u];i!=-1;i=pre[edge[i^1].to]) if(Min>edge[i].cap-edge[i].flow) Min=edge[i].cap-edge[i].flow; for(int i=pre[u];i!=-1;i=pre[edge[i^1].to]) { edge[i].flow+=Min; edge[i^1].flow-=Min; } u=start; ans+=Min; continue; } bool flag=false; int v; for(int i=cur[u];i!=-1;i=edge[i].next) { v=edge[i].to; if(edge[i].cap-edge[i].flow&&dep[v]+1==dep[u]) { flag=true; cur[u]=pre[v]=i; break; } } if(flag) { u=v; continue; } int Min=N; for(int i=head[u];i!=-1;i=edge[i].next) if(edge[i].cap-edge[i].flow&&dep[edge[i].to]<Min) { Min=dep[edge[i].to]; cur[u]=i; } gap[dep[u]]--; if(!gap[dep[u]]) return ans; dep[u]=Min+1; gap[dep[u]]++; if(u!=start) u=edge[pre[u]^1].to; } return ans; } int main() { int n,f,d; int dd[220],ff[220]; while(~scanf("%d%d%d",&n,&f,&d)) { int i,j; init(); for(i=1;i<=f;i++)scanf("%d",&ff[i]); for(i=1;i<=d;i++)scanf("%d",&dd[i]); char str[210]; for(i=1;i<=n;i++) { addedge(i,i+n,1); } for(i=1;i<=n;i++) { scanf("%s",str); getchar(); for(j=0;j<f;j++) { if(str[j]=='Y') { addedge(2*n+j+1,i,1); } } } for(i=1;i<=n;i++) { scanf("%s",str); getchar(); for(j=0;j<d;j++) { if(str[j]=='Y') { addedge(i+n,2*n+f+j+1,1); } } } for(i=2*n+1;i<=2*n+f;i++) { addedge(0,i,ff[i-2*n]); } for(i=2*n+f+1;i<=2*n+f+d;i++) { addedge(i,2*n+f+d+1,dd[i-2*n-f]); } int ans=sap(0,2*n+f+d+1,2*n+f+d+2); cout<<ans<<endl; } return 0; }
D题:
Description
Input
Output
Sample Input
2 2
.m
H.
5 5
HH..m
.....
.....
.....
mm..H
7 8
...H....
...H....
...H....
mmmHmmmm
...H....
...H....
...H....
0 0
Sample Output
2
10
28
HINT
这题建图:
把超级源点和人连起来,cost为0,流量为1
把超级汇点和房子连起来,cost为0,流量为1
把人和房子连起来,cost为他俩的曼哈顿距离(横坐标绝对值之差+纵坐标绝对值之差),流量为1
最小费用流模板
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<vector> #include<queue> #include<malloc.h> using namespace std; typedef long long ll; #define maxn 300 const int MAXN = 10000; const int MAXM = 100000; const int INF = 0x3f3f3f3f; struct Edge { int to,next,cap,flow,cost; } edge[MAXM]; int head[MAXN],tol; int pre[MAXN],dis[MAXN]; bool vis[MAXN]; int N;//节点总个数,节点编号从0~N-1 void init(int n) { N = n; tol = 0;//tol要初始化为0,按位或才能对 memset(head,-1,sizeof(head)); } void addedge(int u,int v,int cap,int cost)//加边 { edge[tol].to = v; edge[tol].cap = cap; edge[tol].cost = cost; edge[tol].flow = 0; edge[tol].next = head[u]; head[u] = tol++; edge[tol].to = u; edge[tol].cap = 0; edge[tol].cost = -cost; edge[tol].flow = 0; edge[tol].next = head[v]; head[v] = tol++; } bool spfa(int s,int t)//找增广路 { queue<int>q; for(int i = 0; i <= t+2; i++) { dis[i] = INF; vis[i] = false; pre[i] = -1; } dis[s] = 0; vis[s] = true; q.push(s); while(!q.empty()) { int u = q.front(); q.pop(); vis[u] = false; for(int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; if(edge[i].cap > edge[i].flow && dis[v] > dis[u] + edge[i].cost ) { dis[v] = dis[u] + edge[i].cost; pre[v] = i; if(!vis[v]) { vis[v] = true; q.push(v); } } } } if(pre[t] == -1)return false; else return true; } //int minCostMaxflow(int s,int t,int &cost) int minCostMaxflow(int s,int t)//最小费用最大流 { int flow = 0; int ans=0; //cost = 0; while(spfa(s,t)) { int Min = INF; for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]) if(Min > edge[i].cap - edge[i].flow) Min = edge[i].cap - edge[i].flow; for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]) { edge[i].flow += Min; edge[i^1].flow -= Min; //cost += edge[i].cost * Min; } ans+=dis[t]; flow += Min; } return ans; } char maze[110][110]; int main() { while(1) { int n,m,j; int nx[110],ny[110],mx[110],my[110]; scanf("%d%d",&n,&m); if(n==0&&m==0)break; memset(head,-1,sizeof head); int i; for(i=1;i<=n;i++) { scanf("%s",maze[i]+1); } int tm=1,th=1; for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { if(maze[i][j]=='m') { nx[tm]=i; ny[tm]=j; tm++; } else if(maze[i][j]=='H') { mx[th]=i; my[th]=j; th++; } } }tm--,th--; init(n); for(i=1;i<=tm;i++) { addedge(0,i,1,0); } for(i=tm+1;i<=tm+th;i++) { addedge(i,tm+th+1,1,0); } for(i=1;i<=tm;i++) { for(j=1;j<=th;j++) { int dis=abs(nx[i]-mx[j])+abs(ny[i]-my[j]); addedge(i,j+tm,1,dis); } } int ans=minCostMaxflow(0,tm+th+1); cout<<ans<<endl; } return 0; }