挑战中
问题编号 |
问题名称 |
问题模型 |
转化模型 |
1 |
飞行员配对方案问题 |
二分图最大匹配 |
网络最大流 |
2 |
太空飞行计划问题 |
最大权闭合图 |
网络最小割 |
3 |
最小路径覆盖问题 |
有向无环图最小路径覆盖 |
网络最大流 |
4 |
魔术球问题 |
有向无环图最小路径覆盖 |
网络最大流 |
5 |
圆桌问题 |
二分图多重匹配 |
网络最大流 |
6 |
最长递增子序列问题 |
最多不相交路径 |
网络最大流 |
7 |
试题库问题 |
二分图多重匹配 |
网络最大流 |
8 |
机器人路径规划问题 |
(未解决) |
最小费用最大流 |
9 |
方格取数问题 |
二分图点权最大独立集 |
网络最小割 |
10 |
餐巾计划问题 |
线性规划网络优化 |
最小费用最大流 |
11 |
航空路线问题 |
最长不相交路径 |
最小费用最大流 |
12 |
软件补丁问题 |
最小转移代价 |
最短路径 |
13 |
星际转移问题 |
网络判定 |
网络最大流 |
14 |
孤岛营救问题 |
分层图最短路径 |
最短路径 |
15 |
汽车加油行驶问题 |
分层图最短路径 |
最短路径 |
16 |
数字梯形问题 |
最大权不相交路径 |
最小费用最大流 |
17 |
运输问题 |
网络费用流量 |
最小费用最大流 |
18 |
分配问题 |
二分图最佳匹配 |
最小费用最大流 |
19 |
负载平衡问题 |
最小代价供求 |
最小费用最大流 |
20 |
深海机器人问题 |
线性规划网络优化 |
最小费用最大流 |
21 |
最长k可重区间集问题 |
最大权不相交路径 |
最小费用最大流 |
22 |
最长k可重线段集问题 |
最大权不相交路径 |
最小费用最大流 |
23 |
火星探险问题 |
线性规划网络优化 |
最小费用最大流 |
24 |
骑士共存问题 |
二分图最大独立集 |
网络最小割 |
1. 飞行员配对方案问题
https://www.oj.swust.edu.cn/problem/show/1736
二分图最大匹配。超级源点连外籍飞行员,容量为1。匹配的外籍和英国飞行员间连一条边,容量为1。英国飞行员与超级汇点连一条边,容量为1。跑最大流,输出匹配的对。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <algorithm> #include <cmath> #include <ctime> #include <vector> #include <queue> #include <map> #include <stack> #include <set> #include <bitset> using namespace std; typedef long long ll; typedef unsigned long long ull; #define ms(a, b) memset(a, b, sizeof(a)) #define pb push_back #define mp make_pair #define pii pair<int, int> #define IOS ios::sync_with_stdio(0);cin.tie(0); #define random(a, b) rand()*rand()%(b-a+1)+a #define pi acos(-1) const ll INF = 0x3f3f3f3f3f3f3f3fll; const int inf = 0x3f3f3f3f; const int maxn = 1000 + 10; const int maxm = 3000000 +10; const int mod = 1000000000; int maze[maxn][maxn]; int gap[maxn],dis[maxn],pre[maxn],cur[maxn]; int sap(int start,int ed,int nodenum){ memset(cur,0,sizeof(cur)); memset(dis,0,sizeof(dis)); memset(gap,0,sizeof(gap)); int u = pre[start]=start,maxflow=0,aug=-1; gap[0]=nodenum; while(dis[start]<nodenum){ loop: for(int v =cur[u];v<nodenum;v++){ if(maze[u][v]&&dis[u]==dis[v]+1){ if(aug==-1||aug>maze[u][v]) aug = maze[u][v]; pre[v]=u; u=cur[u]=v; if(v==ed){ maxflow+=aug; for(u=pre[u];v!=start;v=u,u=pre[u]){ maze[u][v]-=aug; maze[v][u]+=aug; } aug=-1; } goto loop; } } int mindis = nodenum-1; for(int v=0;v<nodenum;v++){ if(maze[u][v]&&mindis>dis[v]){ cur[u]=v; mindis=dis[v]; } } if((--gap[dis[u]])==0) break; gap[dis[u]=mindis+1]++; u=pre[u]; } return maxflow; } int main(){ #ifdef LOCAL freopen("in.txt","r",stdin); #endif // LOCAL int n,m; while(~scanf("%d%d",&m,&n)){ memset(maze,0,sizeof(maze)); for(int i=1;i<=m;i++) maze[0][i]=1; for(int i=m+1;i<=n;i++) maze[i][n+1]=1; int x,y; while(true){ scanf("%d%d",&x,&y); if(x==-1&&y==-1) break; maze[x][y]=1; } int ans=sap(0,n+1,n+2); cout<<ans<<endl; for(int i=1;i<=m;i++){ for(int j=m+1;j<=n;j++){ if(maze[i][j]==0&&maze[j][i]==1){ printf("%d %d ",i,j); break; } } } } return 0; }
2.太空飞行计划问题
https://www.oj.swust.edu.cn/problem/show/1737
有一个有向图,每一个点都有一个权值(可以为正或负或0),选择一个权值和最大的子图,使得每个点的后继都在子图里面,这个子图就叫最大权闭合子图。
这题就是求最大权闭合子图,转化为最小割来解决。把S向正费用点连边,T向负费用点连边,原图的边间连+oo
然后做最小割=min(未选收益+选花费)
答案=选收益-选花费=全选收益-(未选收益+选花费)
最大权闭合图的点就是从起点开始广搜,权值为0的点不走,能走到的点就是被选中的点。
dinic最后一次bfs的dep数组正好可以用来判断这个条件。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <algorithm> #include <cmath> #include <ctime> #include <vector> #include <queue> #include <map> #include <stack> #include <set> #include <bitset> using namespace std; typedef long long ll; typedef unsigned long long ull; #define ms(a, b) memset(a, b, sizeof(a)) #define pb push_back #define mp make_pair #define pii pair<int, int> #define IOS ios::sync_with_stdio(0);cin.tie(0); #define random(a, b) rand()*rand()%(b-a+1)+a #define pi acos(-1) const ll INF = 0x3f3f3f3f3f3f3f3fll; const int inf = 0x3f3f3f3f; const int maxn = 2000 + 10; const int maxm = 1000000 +10; const int mod = 1000000000; struct Edge{ int to,nxt,cap,flow; }edge[maxm]; int tol; int head[maxn]; void init(){ tol = 2; 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].flow=0; edge[tol].nxt=head[u];head[u]=tol++; edge[tol].to=u;edge[tol].cap=rw;edge[tol].flow=0; edge[tol].nxt=head[v];head[v]=tol++; } int Q[maxn]; int dep[maxn],cur[maxn],sta[maxn]; bool bfs(int s,int t,int n){ int fron=0,tail=0; memset(dep,-1,sizeof(dep[0])*(n+1)); dep[s]=0; Q[tail++]=s; while(fron<tail){ int u =Q[fron++]; for(int i=head[u];~i;i=edge[i].nxt){ int v =edge[i].to; if(edge[i].cap>edge[i].flow&&dep[v]==-1){ dep[v]=dep[u]+1; if(v==t) return true; Q[tail++]=v; } } } return false; } int dinic(int s,int t,int n){ int maxflow=0; while(bfs(s,t,n)){ for(int i=0;i<n;i++) cur[i]=head[i]; int u=s,tail=0; while(cur[s]!=-1){ if(u==t){ int tp=inf; for(int i=tail-1;i>=0;i--){ tp=min(tp,edge[sta[i]].cap-edge[sta[i]].flow); } maxflow+=tp; for(int i=tail-1;i>=0;i--){ edge[sta[i]].flow+=tp; edge[sta[i]^1].flow-=tp; if(edge[sta[i]].cap-edge[sta[i]].flow==0) tail=i; } u=edge[sta[tail]^1].to; }else if(cur[u]!=-1&&edge[cur[u]].cap>edge[cur[u]].flow&&dep[u]+1==dep[edge[cur[u]].to]){ sta[tail++]=cur[u]; u=edge[cur[u]].to; }else{ while(u!=s&&cur[u]==-1){ u=edge[sta[--tail]^1].to; } cur[u]=edge[cur[u]].nxt; } } } return maxflow; } int main(){ #ifdef LOCAL freopen("in.txt","r",stdin); #endif // LOCAL int n,m; init(); scanf("%d%d",&m,&n); int sum=0; for(int i=1;i<=m;i++){ int x; char c; scanf("%d",&x); sum+=x; addedge(0,i,x); while(true){ scanf("%d%c",&x,&c); addedge(i,m+x,inf); if(c==' '||c==' ') break; } } for(int i=1;i<=n;i++){ int x; scanf("%d",&x); addedge(i+m,n+m+1,x); } sum-=dinic(0,n+m+1,n+m+2); for(int i=1;i<=m;i++) if(dep[i]!=-1) printf("%d ",i); puts(""); for(int i=1;i<=n;i++) if(dep[i+m]!=-1) printf("%d ",i); puts(""); printf("%d ",sum); return 0; }