做一做网络流,明年加油!
这里写一下题解,写下每一题我的想法。
如果发现哪里写的不对希望能指出来,谢谢!(嘿嘿~ 我觉得可能写的挺对的)
问题编号 |
问题名称 |
问题模型 |
转化模型 |
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 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #define inf 0x3fffffff 6 using namespace std; 7 const int maxn=555; 8 int first[maxn],sign,cur[maxn]; 9 int s,t,d[maxn]; 10 int mp[maxn][maxn]; 11 struct node 12 { 13 int to,w,next; 14 }edge[maxn*50]; 15 void creat() 16 { 17 memset(first,-1,sizeof(first)); 18 sign=0; 19 } 20 void add_edge(int u,int v,int w) 21 { 22 edge[sign].to=v; 23 edge[sign].w=w; 24 edge[sign].next=first[u]; 25 first[u]=sign++; 26 27 edge[sign].to=u; 28 edge[sign].w=0; 29 edge[sign].next=first[v]; 30 first[v]=sign++; 31 } 32 int bfs() 33 { 34 queue<int>q; 35 memset(d,0,sizeof(d)); 36 d[s]=1; 37 q.push(s); 38 while(!q.empty()) 39 { 40 int top=q.front(); 41 q.pop(); 42 for(int i=first[top];~i;i=edge[i].next) 43 { 44 int to=edge[i].to; 45 if(edge[i].w>0&&d[to]==0) 46 { 47 d[to]=d[top]+1; 48 if(to==t) 49 return 1; 50 q.push(to); 51 } 52 } 53 } 54 return d[t]!=0; 55 } 56 int dfs(int top,int flow) 57 { 58 if(top==t) 59 return flow; 60 int ans=0,x=0; 61 for(int i=cur[top];~i;i=edge[i].next) 62 { 63 int to=edge[i].to; 64 if(edge[i].w>0&&d[to]==d[top]+1) 65 { 66 x=dfs(to,min(flow-ans,edge[i].w)); 67 edge[i].w-=x; 68 edge[i^1].w+=x; 69 if(edge[i].w) 70 cur[top] = i; 71 ans+=x; 72 if(ans==flow) 73 return flow; 74 } 75 } 76 if(ans==0) 77 d[top]=0; 78 return ans; 79 } 80 int dinic(int n) 81 { 82 int ans=0; 83 while(bfs()) 84 { 85 for(int i=0;i<=n;i++) 86 cur[i]=first[i]; 87 ans+=dfs(s,inf); 88 } 89 return ans; 90 } 91 int main() 92 { 93 int n,m; 94 scanf("%d%d",&m,&n); 95 creat(); 96 s=0,t=n+m+1; 97 for(int i=1;i<=m;i++) 98 add_edge(s,i,1); 99 for(int i=m+1;i<=n+m;i++) 100 add_edge(i,t,1); 101 int x,y; 102 while(1) 103 { 104 scanf("%d%d",&x,&y); 105 if(x==-1&&y==-1)break; 106 add_edge(x,y,1); 107 } 108 int ans=dinic(t); 109 printf("%d\n",ans); 110 if(ans==0) 111 { 112 printf("No Solution!\n"); 113 return 0; 114 } 115 for(int i=m+1;i<=n+m;i++) 116 for(int j=first[i];~j;j=edge[j].next) 117 if(edge[j].to!=t&&edge[j].w==1) 118 printf("%d %d\n",edge[j].to,i); 119 return 0; 120 }
2、太空飞行计划问题
题目链接:https://www.oj.swust.edu.cn/problem/show/1737
最大权闭合图模型题,最大流最小割。
从起点往每个权值为正的点建立一条边,容量为点权值。
每个权值为负的点往终点建立一条边,容量为权值的绝对值。
如果选A就必须选B 则就从A建立一条往B的边,容量为正无穷。
然后正权值加起来减去最大流就是能选出来最大权闭合图所有点加起来的值
最大权闭合图的点就是从起点开始广搜,权值为0的点不走,能走到的点就是被选中的点。
dinic最后一次bfs的d数组正好可以用来判断这个条件。
代码如下:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 #define inf 0x3fffffff 7 using namespace std; 8 const int maxn=555; 9 int first[maxn],sign,cur[maxn]; 10 int s,t,d[maxn]; 11 struct node 12 { 13 int to,w,next; 14 } edge[maxn*50]; 15 void creat() 16 { 17 memset(first,-1,sizeof(first)); 18 sign=0; 19 } 20 void add_edge(int u,int v,int w) 21 { 22 edge[sign].to=v; 23 edge[sign].w=w; 24 edge[sign].next=first[u]; 25 first[u]=sign++; 26 27 edge[sign].to=u; 28 edge[sign].w=0; 29 edge[sign].next=first[v]; 30 first[v]=sign++; 31 } 32 int bfs() 33 { 34 queue<int>q; 35 memset(d,0,sizeof(d)); 36 d[s]=1; 37 q.push(s); 38 while(!q.empty()) 39 { 40 int top=q.front(); 41 q.pop(); 42 for(int i=first[top]; ~i; i=edge[i].next) 43 { 44 int to=edge[i].to; 45 if(edge[i].w>0&&d[to]==0) 46 { 47 d[to]=d[top]+1; 48 if(to==t) 49 return 1; 50 q.push(to); 51 } 52 } 53 } 54 return d[t]!=0; 55 } 56 int dfs(int top,int flow) 57 { 58 if(top==t) 59 return flow; 60 int ans=0,x=0; 61 for(int i=cur[top]; ~i; i=edge[i].next) 62 { 63 int to=edge[i].to; 64 if(edge[i].w>0&&d[to]==d[top]+1) 65 { 66 x=dfs(to,min(flow-ans,edge[i].w)); 67 edge[i].w-=x; 68 edge[i^1].w+=x; 69 if(edge[i].w) 70 cur[top] = i; 71 ans+=x; 72 if(ans==flow) 73 return flow; 74 } 75 } 76 if(ans==0) 77 d[top]=0; 78 return ans; 79 } 80 int dinic(int n) 81 { 82 int ans=0; 83 while(bfs()) 84 { 85 for(int i=0; i<=n; i++) 86 cur[i]=first[i]; 87 ans+=dfs(s,inf); 88 } 89 return ans; 90 } 91 92 int main() 93 { 94 int n,m; 95 scanf("%d%d",&m,&n); 96 creat(); 97 s=0,t=n+m+1; 98 int sum=0; 99 for(int i=1; i<=m; i++) 100 { 101 int x; 102 char c; 103 scanf("%d",&x); 104 sum+=x; 105 add_edge(s,n+i,x); 106 while(1) 107 { 108 scanf("%d%c",&x,&c); 109 add_edge(n+i,x,inf); 110 if(c=='\n'||c=='\r')break; 111 } 112 } 113 for(int i=1; i<=n; i++) 114 { 115 int w; 116 scanf("%d",&w); 117 add_edge(i,t,w); 118 } 119 sum-=dinic(t); 120 121 for(int i=1; i<=m; i++) 122 { 123 if(d[i+n]) 124 printf("%d ",i); 125 } 126 printf("\n"); 127 for(int i=1; i<=n; i++) 128 if(d[i]) 129 printf("%d ",i); 130 printf("\n"); 131 printf("%d\n",sum); 132 return 0; 133 }
3、最小路径覆盖问题
题目链接:https://loj.ac/problem/6002
最小路径覆盖问题模型,拆点做最大匹配,总点数减去最大流为需要的最少的边数
题目有建图方法
拆点意思是这样的,第i个点的流量流向第j个点时表示第i个点的下一个点是j点,剩下的没办法流向终点的点就是没有点能到它那里,说明这个点是边的起点,根据这个来递归可以用来查找答案方案。
代码如下:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 #define inf 0x3fffffff 7 using namespace std; 8 const int maxn=555; 9 int first[maxn],sign,cur[maxn]; 10 int s,t,d[maxn]; 11 struct node 12 { 13 int to,w,next; 14 } edge[maxn*50]; 15 void creat() 16 { 17 memset(first,-1,sizeof(first)); 18 sign=0; 19 } 20 void add_edge(int u,int v,int w) 21 { 22 edge[sign].to=v; 23 edge[sign].w=w; 24 edge[sign].next=first[u]; 25 first[u]=sign++; 26 27 edge[sign].to=u; 28 edge[sign].w=0; 29 edge[sign].next=first[v]; 30 first[v]=sign++; 31 } 32 int bfs() 33 { 34 queue<int>q; 35 memset(d,0,sizeof(d)); 36 d[s]=1; 37 q.push(s); 38 while(!q.empty()) 39 { 40 int top=q.front(); 41 q.pop(); 42 for(int i=first[top]; ~i; i=edge[i].next) 43 { 44 int to=edge[i].to; 45 if(edge[i].w>0&&d[to]==0) 46 { 47 d[to]=d[top]+1; 48 if(to==t) 49 return 1; 50 q.push(to); 51 } 52 } 53 } 54 return d[t]!=0; 55 } 56 int dfs(int top,int flow) 57 { 58 if(top==t) 59 return flow; 60 int ans=0,x=0; 61 for(int i=cur[top]; ~i; i=edge[i].next) 62 { 63 int to=edge[i].to; 64 if(edge[i].w>0&&d[to]==d[top]+1) 65 { 66 x=dfs(to,min(flow-ans,edge[i].w)); 67 edge[i].w-=x; 68 edge[i^1].w+=x; 69 if(edge[i].w) 70 cur[top] = i; 71 ans+=x; 72 if(ans==flow) 73 return flow; 74 } 75 } 76 if(ans==0) 77 d[top]=0; 78 return ans; 79 } 80 int dinic(int n) 81 { 82 int ans=0; 83 while(bfs()) 84 { 85 for(int i=0; i<=n; i++) 86 cur[i]=first[i]; 87 ans+=dfs(s,inf); 88 } 89 return ans; 90 } 91 int n,m; 92 int vis[maxn]; 93 int go(int x,int &f) 94 { 95 int loc=x+n; 96 vis[x]=1; 97 for(int i=first[loc]; ~i; i=edge[i].next) 98 if(edge[i].w==1&&edge[i].to!=n*2+1) 99 go(edge[i].to,f); 100 if(f==1)f=0; 101 else putchar(' '); 102 printf("%d",x); 103 } 104 int main() 105 { 106 scanf("%d%d",&n,&m); 107 creat(); 108 s=0,t=n*2+1; 109 for(int i=1; i<=n; i++) 110 add_edge(s,i,1),add_edge(i+n,t,1); 111 int x,y; 112 while(m--) 113 scanf("%d%d",&x,&y),add_edge(x,y+n,1); 114 int ans=n-dinic(t); 115 for(int i=first[t]; ~i; i=edge[i].next) 116 if(edge[i].w==1&&!vis[edge[i].to-n]) 117 { 118 int f=1; 119 go(edge[i].to-n,f); 120 puts(""); 121 } 122 for(int i=1;i<=n;i++) 123 if(!vis[i])printf("%d\n",i); 124 printf("%d\n",ans); 125 return 0; 126 }
4、魔术球问题
题目链接:https://www.oj.swust.edu.cn/problem/show/1739
每个点能连接的下一个点可以连成一个链,给你1-m个点,求出来的最小路径覆盖的答案就是需要的桩子个数(画重点!最小路径覆盖)
因为网络流是可以在残量网络上继续跑的,所以我们每次就多增加一个点的边,然后一直跑网络流,直到需要的桩子个数刚好超过题目给的桩子个数时就是最多的球的个数了,我们再按照上面那题的方法递归输出答案即可。
1 #include<cstdio> 2 #include<cmath> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 #include<queue> 7 #define inf 0x3fffffff 8 using namespace std; 9 const int maxn=11111; 10 int first[maxn],sign,cur[maxn]; 11 int s,t,d[maxn]; 12 struct node 13 { 14 int to,w,next; 15 } edge[maxn*20]; 16 void creat() 17 { 18 memset(first,-1,sizeof(first)); 19 sign=0; 20 } 21 void add_edge(int u,int v,int w) 22 { 23 edge[sign].to=v; 24 edge[sign].w=w; 25 edge[sign].next=first[u]; 26 first[u]=sign++; 27 28 edge[sign].to=u; 29 edge[sign].w=0; 30 edge[sign].next=first[v]; 31 first[v]=sign++; 32 } 33 int bfs() 34 { 35 queue<int>q; 36 memset(d,0,sizeof(d)); 37 d[s]=1; 38 q.push(s); 39 while(!q.empty()) 40 { 41 int top=q.front(); 42 q.pop(); 43 for(int i=first[top]; ~i; i=edge[i].next) 44 { 45 int to=edge[i].to; 46 if(edge[i].w>0&&d[to]==0) 47 { 48 d[to]=d[top]+1; 49 if(to==t) 50 return 1; 51 q.push(to); 52 } 53 } 54 } 55 return d[t]!=0; 56 } 57 int dfs(int top,int flow) 58 { 59 if(top==t) 60 return flow; 61 int ans=0,x=0; 62 for(int i=cur[top]; ~i; i=edge[i].next) 63 { 64 int to=edge[i].to; 65 if(edge[i].w>0&&d[to]==d[top]+1) 66 { 67 x=dfs(to,min(flow-ans,edge[i].w)); 68 edge[i].w-=x; 69 edge[i^1].w+=x; 70 if(edge[i].w) 71 cur[top] = i; 72 ans+=x; 73 if(ans==flow) 74 return flow; 75 } 76 } 77 if(ans==0) 78 d[top]=0; 79 return ans; 80 } 81 int dinic(int n) 82 { 83 int ans=0; 84 while(bfs()) 85 { 86 for(int i=0; i<=n; i++) 87 cur[i]=first[i]; 88 ans+=dfs(s,inf); 89 } 90 return ans; 91 } 92 int id[11111],wid[11111]; 93 int vis[maxn]; 94 int go(int x,int &f) 95 { 96 int loc=wid[x]; 97 vis[x]=1; 98 for(int i=first[loc]; ~i; i=edge[i].next) 99 if(edge[i].w==1&&edge[i].to!=t) 100 go(edge[i].to/2,f); 101 if(f==1)f=0; 102 else putchar(' '); 103 printf("%d",x); 104 } 105 106 int main() 107 { 108 creat(); 109 int n; 110 scanf("%d",&n); 111 s=0,t=11110; 112 int cnt=2; 113 int ans=0,i=0; 114 for(i=1; i-ans<=n+1; i++) 115 { 116 id[i]=cnt++; 117 wid[i]=cnt++; 118 add_edge(s,id[i],1); 119 add_edge(wid[i],t,1); 120 for(int j=1; j<i; j++) 121 { 122 int se=sqrt(i+j); 123 if(se*se==i+j) 124 { 125 add_edge(id[j],wid[i],1); 126 } 127 } 128 ans+=dinic(t); 129 } 130 int as=i-2; 131 printf("%d\n",i-2); 132 for(i=first[t]; ~i; i=edge[i].next) 133 if(edge[i].w==1&&!vis[edge[i].to/2]) 134 { 135 int f=1; 136 go(edge[i].to/2,f); 137 puts(""); 138 } 139 for(i=1;i<=as;i++) 140 if(!vis[i])printf("%d\n",i); 141 return 0; 142 }
5、圆桌问题
题目链接:https://www.oj.swust.edu.cn/problem/show/1740
这个挺水的,感觉就是暴力建图跑个最大流就可以了。
起点往每个单位的点建立一个边,边的容量就是单位的人数。
每个单位对每个餐桌拆一个点出来限制一下流量,让每个单位去每个餐桌只能有1的流量。
然后每个餐桌往终点建立一个边,容量为餐桌的容量。
这样跑个最大流,然后看结果是不是和单位人数相同就行,不同的话就不可行。
输出答案的话就找那个限制流量的边,如果那个边走过了就说明这个点有用嘛,找一下输出就行。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 #define inf 0x3fffffff 7 using namespace std; 8 const int maxn=51111; 9 int first[maxn],sign,cur[maxn]; 10 int s,t,d[maxn]; 11 struct node 12 { 13 int to,w,next; 14 } edge[maxn*20]; 15 void creat() 16 { 17 memset(first,-1,sizeof(first)); 18 sign=0; 19 } 20 void add_edge(int u,int v,int w) 21 { 22 edge[sign].to=v; 23 edge[sign].w=w; 24 edge[sign].next=first[u]; 25 first[u]=sign++; 26 27 edge[sign].to=u; 28 edge[sign].w=0; 29 edge[sign].next=first[v]; 30 first[v]=sign++; 31 } 32 int bfs() 33 { 34 queue<int>q; 35 memset(d,0,sizeof(d)); 36 d[s]=1; 37 q.push(s); 38 while(!q.empty()) 39 { 40 int top=q.front(); 41 q.pop(); 42 for(int i=first[top]; ~i; i=edge[i].next) 43 { 44 int to=edge[i].to; 45 if(edge[i].w>0&&d[to]==0) 46 { 47 d[to]=d[top]+1; 48 if(to==t) 49 return 1; 50 q.push(to); 51 } 52 } 53 } 54 return d[t]!=0; 55 } 56 int dfs(int top,int flow) 57 { 58 if(top==t) 59 return flow; 60 int ans=0,x=0; 61 for(int i=cur[top]; ~i; i=edge[i].next) 62 { 63 int to=edge[i].to; 64 if(edge[i].w>0&&d[to]==d[top]+1) 65 { 66 x=dfs(to,min(flow-ans,edge[i].w)); 67 edge[i].w-=x; 68 edge[i^1].w+=x; 69 if(edge[i].w) 70 cur[top] = i; 71 ans+=x; 72 if(ans==flow) 73 return flow; 74 } 75 } 76 if(ans==0) 77 d[top]=0; 78 return ans; 79 } 80 int dinic(int n) 81 { 82 int ans=0; 83 while(bfs()) 84 { 85 for(int i=0; i<=n; i++) 86 cur[i]=first[i]; 87 ans+=dfs(s,inf); 88 } 89 return ans; 90 } 91 vector<int>as[maxn]; 92 int main() 93 { 94 creat(); 95 int n,m,sum=0; 96 scanf("%d%d",&n,&m);///n 单位数 m餐桌数 97 s=0;t=n+m+n*m+1; 98 for(int i=1,x;i<=n;i++) 99 scanf("%d",&x),add_edge(s,i,x),sum+=x; 100 for(int i=1;i<=n;i++) 101 for(int j=1;j<=m;j++) 102 add_edge(i,n+(i-1)*m+j,1),add_edge(n+(i-1)*m+j,j+n+n*m,1); 103 for(int i=1,x;i<=m;i++) 104 scanf("%d",&x),add_edge(i+n+n*m,t,x); 105 if(dinic(t)==sum) 106 puts("1"); 107 else 108 { 109 puts("0"); 110 return 0; 111 } 112 for(int i=1;i<=m;i++) 113 { 114 int f=1; 115 for(int j=first[i+n+n*m];~j;j=edge[j].next) 116 if(edge[j].to!=t&&edge[j].w==1) 117 { 118 as[(edge[j].to-n-i)/m+1].push_back(i); 119 } 120 } 121 for(int i=1;i<=n;i++) 122 { 123 printf("%d",as[i][0]); 124 for(int j=1;j<as[i].size();j++) 125 printf(" %d",as[i][j]); 126 printf("\n"); 127 } 128 return 0; 129 }
6、最长递增子序列问题
题目链接:https://www.oj.swust.edu.cn/problem/show/1741
这题第一问随便求,n方或者nlogn的dp都可以,难点在第二问和第三问,一开始我没注意到第二问和第三问的询问s是第一问的答案,以为是随便询问一个长度,后来发现是求第一问的答案,这样就简单点了。
建图方案:
把每个点拆成两个点,拆点是为了让每个点只走一次。
起点每次连到dp出来答案等于s的点,容量为1。
然后dp长度为1的点连到终点,容量也是1。
具体的不好说,看我代码吧,i和i+n是一对点,跑出来的最大流就直接是答案。
因为拆点是为了限制每个点只用一次,所以第三问就把1这个点的能用次数改成无穷大,最后一个点也是,然后再跑一下最大流就好。
代码如下:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 #define inf 0x3fffffff 7 using namespace std; 8 const int maxn=888; 9 int first[maxn],sign,cur[maxn]; 10 int s,t,d[maxn]; 11 struct node 12 { 13 int to,w,next; 14 } edge[maxn*20]; 15 void creat() 16 { 17 memset(first,-1,sizeof(first)); 18 sign=0; 19 } 20 void add_edge(int u,int v,int w) 21 { 22 edge[sign].to=v; 23 edge[sign].w=w; 24 edge[sign].next=first[u]; 25 first[u]=sign++; 26 27 edge[sign].to=u; 28 edge[sign].w=0; 29 edge[sign].next=first[v]; 30 first[v]=sign++; 31 } 32 int bfs() 33 { 34 queue<int>q; 35 memset(d,0,sizeof(d)); 36 d[s]=1; 37 q.push(s); 38 while(!q.empty()) 39 { 40 int top=q.front(); 41 q.pop(); 42 for(int i=first[top]; ~i; i=edge[i].next) 43 { 44 int to=edge[i].to; 45 if(edge[i].w>0&&d[to]==0) 46 { 47 d[to]=d[top]+1; 48 if(to==t) 49 return 1; 50 q.push(to); 51 } 52 } 53 } 54 return d[t]!=0; 55 } 56 int dfs(int top,int flow) 57 { 58 if(top==t) 59 return flow; 60 int ans=0,x=0; 61 for(int i=cur[top]; ~i; i=edge[i].next) 62 { 63 int to=edge[i].to; 64 if(edge[i].w>0&&d[to]==d[top]+1) 65 { 66 x=dfs(to,min(flow-ans,edge[i].w)); 67 edge[i].w-=x; 68 edge[i^1].w+=x; 69 if(edge[i].w) 70 cur[top] = i; 71 ans+=x; 72 if(ans==flow) 73 return flow; 74 } 75 } 76 if(ans==0) 77 d[top]=0; 78 return ans; 79 } 80 int dinic(int n) 81 { 82 int ans=0; 83 while(bfs()) 84 { 85 for(int i=0; i<=n; i++) 86 cur[i]=first[i]; 87 ans+=dfs(s,inf); 88 } 89 return ans; 90 } 91 int a[maxn],f[maxn]; 92 int main() 93 { 94 creat(); 95 int n; 96 scanf("%d",&n); 97 for(int i=1;i<=n;i++) 98 scanf("%d",a+i),f[i]=1; 99 for(int i=1;i<=n;i++) 100 for(int j=1;j<i;j++) 101 if(a[i]>a[j])f[i]=max(f[i],f[j]+1); 102 int ans=0; 103 for(int i=1;i<=n;i++) 104 ans=max(ans,f[i]); 105 s=0,t=n*2+1; 106 for(int i=1;i<=n;i++) 107 if(f[i]==ans) 108 add_edge(s,i,1); 109 for(int i=1;i<=n;i++) 110 add_edge(i+n,i,1); 111 for(int i=1;i<=n;i++) 112 if(f[i]==1) 113 add_edge(i,t,1); 114 for(int i=1;i<=n;i++) 115 for(int j=1;j<i;j++) 116 if(a[i]>a[j]&&f[i]==f[j]+1) 117 add_edge(i,j+n,1); 118 int sum=dinic(t); 119 printf("%d\n%d\n",ans,sum); 120 add_edge(1+n,1,inf); 121 add_edge(1,t,inf); 122 if(f[n]==ans) 123 add_edge(s,n,inf); 124 sum+=dinic(t); 125 printf("%d\n",sum); 126 return 0; 127 }
7、试题库问题
题目链接:https://www.oj.swust.edu.cn/problem/show/1742
建图,就是普通的匹配吧,起点往每个类型建立边,容量为需要的这个类型的试题数量。
然后对每个关系建立一条边,然后把每个题目和终点再连一条边,容量为1,跑出来的最大流就是答案。
代码如下:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 #define inf 0x3fffffff 7 using namespace std; 8 const int maxn=1777; 9 int first[maxn],sign,cur[maxn]; 10 int s,t,d[maxn]; 11 struct node 12 { 13 int to,w,next; 14 } edge[maxn*20]; 15 void creat() 16 { 17 memset(first,-1,sizeof(first)); 18 sign=0; 19 } 20 void add_edge(int u,int v,int w) 21 { 22 edge[sign].to=v; 23 edge[sign].w=w; 24 edge[sign].next=first[u]; 25 first[u]=sign++; 26 27 edge[sign].to=u; 28 edge[sign].w=0; 29 edge[sign].next=first[v]; 30 first[v]=sign++; 31 } 32 int bfs() 33 { 34 queue<int>q; 35 memset(d,0,sizeof(d)); 36 d[s]=1; 37 q.push(s); 38 while(!q.empty()) 39 { 40 int top=q.front(); 41 q.pop(); 42 for(int i=first[top]; ~i; i=edge[i].next) 43 { 44 int to=edge[i].to; 45 if(edge[i].w>0&&d[to]==0) 46 { 47 d[to]=d[top]+1; 48 if(to==t) 49 return 1; 50 q.push(to); 51 } 52 } 53 } 54 return d[t]!=0; 55 } 56 int dfs(int top,int flow) 57 { 58 if(top==t) 59 return flow; 60 int ans=0,x=0; 61 for(int i=cur[top]; ~i; i=edge[i].next) 62 { 63 int to=edge[i].to; 64 if(edge[i].w>0&&d[to]==d[top]+1) 65 { 66 x=dfs(to,min(flow-ans,edge[i].w)); 67 edge[i].w-=x; 68 edge[i^1].w+=x; 69 if(edge[i].w) 70 cur[top] = i; 71 ans+=x; 72 if(ans==flow) 73 return flow; 74 } 75 } 76 if(ans==0) 77 d[top]=0; 78 return ans; 79 } 80 int dinic(int n) 81 { 82 int ans=0; 83 while(bfs()) 84 { 85 for(int i=0; i<=n; i++) 86 cur[i]=first[i]; 87 ans+=dfs(s,inf); 88 } 89 return ans; 90 } 91 int a[maxn],f[maxn]; 92 int main() 93 { 94 creat(); 95 int k,n; 96 int sum=0; 97 scanf("%d%d",&k,&n); 98 s=0,t=n+k+1; 99 for(int i=1;i<=n;i++) 100 add_edge(i,t,1); 101 for(int i=1,x;i<=k;i++) 102 scanf("%d",&x),add_edge(s,n+i,x),sum+=x; 103 for(int i=1;i<=n;i++) 104 { 105 int m,x; 106 scanf("%d",&m); 107 while(m--) 108 { 109 scanf("%d",&x); 110 add_edge(n+x,i,1); 111 } 112 } 113 if(dinic(t)!=sum) 114 { 115 printf("No Solution!\n"); 116 return 0; 117 } 118 for(int i=1;i<=k;i++) 119 { 120 printf("%d:",i); 121 for(int j=first[n+i];~j;j=edge[j].next) 122 if(edge[j].to!=s&&!edge[j].w) 123 { 124 printf(" %d",edge[j].to); 125 } 126 printf("\n"); 127 } 128 return 0; 129 }
8、机器人路径规划问题
太难了,不会,跳过,而且网上说这题不是网络流题。
9、方格取数问题
题目链接:https://www.oj.swust.edu.cn/problem/show/1744
建图,把图分成两部分,变成二分图,然后找出最小割,用所有的和减去最小割就是答案。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 #define inf 0x3fffffff 7 using namespace std; 8 const int maxn=2777; 9 int first[maxn],sign,cur[maxn]; 10 int s,t,d[maxn]; 11 struct node 12 { 13 int to,w,next; 14 } edge[maxn*20]; 15 void creat() 16 { 17 memset(first,-1,sizeof(first)); 18 sign=0; 19 } 20 void add_edge(int u,int v,int w) 21 { 22 edge[sign].to=v; 23 edge[sign].w=w; 24 edge[sign].next=first[u]; 25 first[u]=sign++; 26 27 edge[sign].to=u; 28 edge[sign].w=0; 29 edge[sign].next=first[v]; 30 first[v]=sign++; 31 } 32 int bfs() 33 { 34 queue<int>q; 35 memset(d,0,sizeof(d)); 36 d[s]=1; 37 q.push(s); 38 while(!q.empty()) 39 { 40 int top=q.front(); 41 q.pop(); 42 for(int i=first[top]; ~i; i=edge[i].next) 43 { 44 int to=edge[i].to; 45 if(edge[i].w>0&&d[to]==0) 46 { 47 d[to]=d[top]+1; 48 if(to==t) 49 return 1; 50 q.push(to); 51 } 52 } 53 } 54 return d[t]!=0; 55 } 56 int dfs(int top,int flow) 57 { 58 if(top==t) 59 return flow; 60 int ans=0,x=0; 61 for(int i=cur[top]; ~i; i=edge[i].next) 62 { 63 int to=edge[i].to; 64 if(edge[i].w>0&&d[to]==d[top]+1) 65 { 66 x=dfs(to,min(flow-ans,edge[i].w)); 67 edge[i].w-=x; 68 edge[i^1].w+=x; 69 if(edge[i].w) 70 cur[top] = i; 71 ans+=x; 72 if(ans==flow) 73 return flow; 74 } 75 } 76 if(ans==0) 77 d[top]=0; 78 return ans; 79 } 80 int dinic(int n) 81 { 82 int ans=0; 83 while(bfs()) 84 { 85 for(int i=0; i<=n; i++) 86 cur[i]=first[i]; 87 ans+=dfs(s,inf); 88 } 89 return ans; 90 } 91 int a[111][111]; 92 int go[4][2]= {1,0,0,1,-1,0,0,-1}; 93 int main() 94 { 95 int n,m; 96 scanf("%d%d",&n,&m); 97 creat(); 98 int sum=0; 99 for(int i=1; i<=n; i++) 100 for(int j=1; j<=m; j++) 101 scanf("%d",&a[i][j]),sum+=a[i][j]; 102 s=0,t=n*m+1; 103 for(int i=1; i<=n; i++) 104 for(int j=1; j<=m; j++) 105 if((i+j)&1) 106 add_edge(s,(i-1)*m+j,a[i][j]); 107 else add_edge((i-1)*m+j,t,a[i][j]); 108 for(int i=1; i<=n; i++) 109 for(int j=1; j<=m; j++) 110 if((i+j)&1) 111 for(int k=0; k<4; k++) 112 { 113 int x=i+go[k][0],y=j+go[k][1]; 114 if(x>=1&&x<=n&&y>=1&&y<=m) 115 { 116 int res=(x-1)*m+y; 117 add_edge((i-1)*m+j,res,inf); 118 } 119 } 120 printf("%d\n",sum-dinic(t)); 121 return 0; 122 }
10、餐巾计划问题
题目链接:https://www.oj.swust.edu.cn/problem/show/1745
这个题是个费用流,建图比较神奇,我一开始想了好久,就是不知道该怎么建图,当时感觉就光让我跑最大流我都不一定会跑。后来乱画画好像画出来了?(一脸懵逼
建图大概是这样,s为起点,t为终点,然后s连接到 i 有一条容量为a[i],费用为0的边,这个流量是今天用完了的毛巾,然后s连接到 i' 有一条容量为a[i],费用为p的边,然后 i' 到t又有一条容量为a[i],费用为0的边。然后剩下的按照题意加几个转移的边就好了。
分析一下:因为有一条s-i'-t的路径,容量都是a[i],所以i'到t一定是满流的,说明今天一定会用a[i]条毛巾。所以s-i每次流过去的a[i]就是今天用完剩下来的毛巾(精髓),这样的话i'到t的流量有两种来源:
1、用p的价格来买的毛巾
2、前几天的剩下的毛巾转移过来的流量
然后一波转移猛如虎这个题就能A了。具体实现看代码吧,感觉还是很难想的
洛谷上数据比较强 要开long long
1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 #include <string> 5 #include <cstdio> 6 #include <cmath> 7 #include <queue> 8 #include <map> 9 #include <set> 10 #define eps 1e-5 11 #define MAXN 2555 12 #define MAXM 55555 13 #define INF 1000000007 14 using namespace std; 15 struct EDGE 16 { 17 int cost, cap, v; 18 int next, re; 19 }edge[MAXM]; 20 int head[MAXN], e; 21 int vis[MAXN]; 22 int ans, cost, st, ed, maxflow; 23 void creat() 24 { 25 memset(head, -1, sizeof(head)); 26 e = 0; 27 ans = cost = maxflow = 0; 28 } 29 void add_edge(int u, int v, int cap, int cost) 30 { 31 edge[e].v = v; 32 edge[e].cap = cap; 33 edge[e].cost = cost; 34 edge[e].re = e + 1; 35 edge[e].next = head[u]; 36 head[u] = e++; 37 edge[e].v = u; 38 edge[e].cap = 0; 39 edge[e].cost = -cost; 40 edge[e].re = e - 1; 41 edge[e].next = head[v]; 42 head[v] = e++; 43 } 44 int aug(int u, int f) 45 { 46 if(u == ed) 47 { 48 ans += cost * f; 49 maxflow += f; 50 return f; 51 } 52 vis[u] = 1; 53 int tmp = f; 54 for(int i = head[u]; i != -1; i = edge[i].next) 55 if(edge[i].cap && !edge[i].cost && !vis[edge[i].v]) 56 { 57 int delta = aug(edge[i].v, tmp < edge[i].cap ? tmp : edge[i].cap); 58 edge[i].cap -= delta; 59 edge[edge[i].re].cap += delta; 60 tmp -= delta; 61 if(!tmp) return f; 62 } 63 return f - tmp; 64 } 65 bool modlabel(int n) 66 { 67 int delta = INF; 68 for(int u = 1; u <= n; u++) 69 if(vis[u]) 70 for(int i = head[u]; i != -1; i = edge[i].next) 71 if(edge[i].cap && !vis[edge[i].v] && edge[i].cost < delta) delta = edge[i].cost; 72 if(delta == INF) return false; 73 for(int u = 1; u <= n; u++) 74 if(vis[u]) 75 for(int i = head[u]; i != -1; i = edge[i].next) 76 edge[i].cost -= delta, edge[edge[i].re].cost += delta; 77 cost += delta; 78 return true; 79 } 80 int costflow(int n) 81 { 82 do 83 { 84 do 85 { 86 memset(vis, 0, sizeof(vis)); 87 }while(aug(st, INF)); 88 }while(modlabel(n)); 89 return ans; 90 } 91 int a[MAXN]; 92 int main() 93 { 94 int N,p,m,f,n,s; 95 creat(); 96 scanf("%d%d%d%d%d%d",&N,&p,&m,&f,&n,&s); 97 for(int i=1;i<=N;i++) 98 scanf("%d",a+i); 99 st=N*2+1,ed=N*2+2; 100 for(int i=1;i<=N;i++) 101 { 102 add_edge(st,i,a[i],0); 103 add_edge(st,i+N,a[i],p); 104 if(i<N) 105 add_edge(i,i+1,INF,0); 106 if(i+m<=N) 107 add_edge(i,i+N+m,INF,f); 108 if(i+n<=N) 109 add_edge(i,i+N+n,INF,s); 110 add_edge(i+N,ed,a[i],0); 111 } 112 printf("%d\n",costflow(ed)); 113 return 0; 114 }
11、航空路线问题
题目链接:https://www.oj.swust.edu.cn/problem/show/1746
建图,一个简单的费用流,拆点限制每个点只能走一次,然后费用用负数,跑个最大费用最大流就好了。
题目有个坑,1-n可以直接走且最大流小于2的时候需要你手动特判一下答案,其他的就没啥问题了。
1 #include <cstdio> 2 #include <algorithm> 3 #include <cmath> 4 #include <cstring> 5 #include <iostream> 6 #include <ctime> 7 #include <map> 8 #include <queue> 9 #include <cstdlib> 10 #include <string> 11 #include <climits> 12 #include <set> 13 #include <vector> 14 #include <unordered_map> 15 using namespace std; 16 unordered_map<string,int>mp; 17 unordered_map<int,string>mps; 18 const int MAXN=201; 19 const int MAXM=2001; 20 bool vis[MAXN];int dist[MAXN]; 21 int s,t,ans; 22 23 int first[MAXN],sign; 24 struct EDGE 25 { 26 int p,c,cc,next; 27 }edge[MAXM]; 28 29 void creat() 30 { 31 memset(first,-1,sizeof(first)); 32 sign=ans=0; 33 } 34 35 void add_edge(int from,int to,int c,int cost) 36 { 37 edge[sign].p=to; 38 edge[sign].c=c; 39 edge[sign].cc=cost; 40 edge[sign].next=first[from]; 41 first[from]=sign++; 42 edge[sign].p=from; 43 edge[sign].c=0; 44 edge[sign].cc=-cost; 45 edge[sign].next=first[to]; 46 first[to]=sign++; 47 } 48 49 inline bool spfa(int n,int s,int t){ 50 memset(vis,0,sizeof vis); 51 for(int i=0;i<=n;i++)dist[i]=1e9;dist[t]=0;vis[t]=1; 52 deque<int>q;q.push_back(t); 53 while(!q.empty()){ 54 int now=q.front();q.pop_front(); 55 for(int k=first[now];k>-1;k=edge[k].next)if(edge[k^1].c&&dist[edge[k].p]>dist[now]-edge[k].cc){ 56 dist[edge[k].p]=dist[now]-edge[k].cc; 57 if(!vis[edge[k].p]){ 58 vis[edge[k].p]=1; 59 if(!q.empty()&&dist[edge[k].p]<dist[q.front()])q.push_front(edge[k].p);else q.push_back(edge[k].p); 60 } 61 } 62 vis[now]=0; 63 } 64 return dist[s]<1e9; 65 } 66 inline int dfs(int x,int low){ 67 if(x==t){vis[t]=1;return low;} 68 int used=0,a;vis[x]=1; 69 for(int k=first[x];k>-1;k=edge[k].next)if(!vis[edge[k].p]&&edge[k].c&&dist[x]-edge[k].cc==dist[edge[k].p]){ 70 a=dfs(edge[k].p,min(edge[k].c,low-used)); 71 if(a)ans+=a*edge[k].cc,edge[k].c-=a,edge[k^1].c+=a,used+=a; 72 if(used==low)break; 73 } 74 return used; 75 } 76 inline int costflow(int n){ 77 int flow=0; 78 while(spfa(n,s,t)){ 79 vis[t]=1; 80 while(vis[t]){ 81 memset(vis,0,sizeof vis); 82 flow+=dfs(s,1e9); 83 } 84 } 85 return flow; 86 } 87 int vs[222]; 88 char tmp[22],tmp2[22]; 89 int main() 90 { 91 creat(); 92 int n,m; 93 scanf("%d%d",&n,&m); 94 for(int i=1; i<=n; i++) 95 { 96 scanf("%s",tmp); 97 mp[tmp]=i; 98 mps[i]=tmp; 99 } 100 s=1,t=n*2; 101 for(int i=2;i<n;i++) 102 add_edge(i,i+n,1,-1); 103 add_edge(1,1+n,2,-1); 104 add_edge(n,n+n,2,-1); 105 int f=0; 106 while(m--) 107 { 108 scanf("%s%s",tmp,tmp2); 109 int x=mp[tmp],y=mp[tmp2]; 110 if(x>y)swap(x,y); 111 if(x==1&&y==n)f=1; 112 add_edge(x+n,y,1,0); 113 } 114 int maxf=costflow(t); 115 if(f==1&&maxf==1) 116 { 117 printf("2\n"); 118 cout<<mps[1]<<endl<<mps[n]<<endl<<mps[1]<<endl; 119 return 0; 120 } 121 if(maxf<2) 122 { 123 printf("No Solution!\n"); 124 return 0; 125 } 126 127 printf("%d\n",-ans-2); 128 int now=1+n; 129 while(now!=n+n) 130 { 131 cout<<mps[now-n]<<endl; 132 for(int i=first[now];~i;i=edge[i].next) 133 if(edge[i].p!=now-n&&edge[i].c==0) 134 { 135 now=edge[i].p+n; 136 vs[edge[i].p]=1; 137 break; 138 } 139 } 140 now-=n; 141 while(now!=1) 142 { 143 cout<<mps[now]<<endl; 144 for(int i=first[now];~i;i=edge[i].next) 145 if(edge[i].c) 146 if(!vs[edge[i].p-n]) 147 now=edge[i].p-n; 148 } 149 cout<<mps[1]<<endl; 150 return 0; 151 }
12、软件补丁问题
题目链接:https://www.oj.swust.edu.cn/problem/show/1747
这题状态压缩跑个最短路就好了,你们感受一下就懂了233
#include<stdio.h> #include<string.h> #include<queue> using namespace std; const int inf=1e9; const int maxn=1<<20; int n,m,dij[maxn],vis[maxn]; int b1[111],b2[111],f1[111],f2[111],w[111]; int dijstra(int s,int t) { priority_queue<pair<int,int> >q; int i; for(i=0; i<maxn; i++)dij[i]=inf; dij[s]=0; q.push(make_pair(-dij[s],s)); while(!q.empty()) { int now=q.top().second; q.pop(); if(!vis[now]) { if(now==t)return dij[t]; vis[now]=1; for(i=1;i<=m;i++) if((now&b1[i])==b1[i]&&(now&b2[i])==0) { int to=now; to&=(~f1[i]); to|=f2[i]; if(dij[to]>dij[now]+w[i]) { dij[to]=dij[now]+w[i]; q.push(make_pair(-dij[to],to)); } } } } return 0; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { char tmp[22]; scanf("%d%s",w+i,tmp); for(int k=0;tmp[k];k++) { if(tmp[k]=='+') b1[i]|=1<<k; else if(tmp[k]=='-') b2[i]|=1<<k; } scanf("%s",tmp); for(int k=0;tmp[k];k++) { if(tmp[k]=='-') f1[i]|=1<<k; else if(tmp[k]=='+') f2[i]|=1<<k; } } printf("%d\n",dijstra((1<<n)-1,0)); }
13、星际转移问题
题目链接:https://www.oj.swust.edu.cn/problem/show/1748
建图:这题数据比较小,可以暴力一点建图(一开始怕超时都没敢写 结果意外的跑的挺快)
对每一秒都建一层图,然后正常的转移就好了,每次都往后一秒转移,每一层都有一个节点连接到终点,大概就是这样。
然后题目还要判断走不到的情况,这个就是判断两点能否到达嘛,并查集一下就好了,看地球和月亮是否被并在了一起就行。
代码如下:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 #define inf 0x3fffffff 7 using namespace std; 8 const int maxn=8888; 9 int first[maxn],sign,cur[maxn]; 10 int s,t,d[maxn]; 11 struct node 12 { 13 int to,w,next; 14 } edge[maxn*20]; 15 void creat() 16 { 17 memset(first,-1,sizeof(first)); 18 sign=0; 19 } 20 void add_edge(int u,int v,int w) 21 { 22 edge[sign].to=v; 23 edge[sign].w=w; 24 edge[sign].next=first[u]; 25 first[u]=sign++; 26 27 edge[sign].to=u; 28 edge[sign].w=0; 29 edge[sign].next=first[v]; 30 first[v]=sign++; 31 } 32 int bfs() 33 { 34 queue<int>q; 35 memset(d,0,sizeof(d)); 36 d[s]=1; 37 q.push(s); 38 while(!q.empty()) 39 { 40 int top=q.front(); 41 q.pop(); 42 for(int i=first[top]; ~i; i=edge[i].next) 43 { 44 int to=edge[i].to; 45 if(edge[i].w>0&&d[to]==0) 46 { 47 d[to]=d[top]+1; 48 if(to==t) 49 return 1; 50 q.push(to); 51 } 52 } 53 } 54 return d[t]!=0; 55 } 56 int dfs(int top,int flow) 57 { 58 if(top==t) 59 return flow; 60 int ans=0,x=0; 61 for(int i=cur[top]; ~i; i=edge[i].next) 62 { 63 int to=edge[i].to; 64 if(edge[i].w>0&&d[to]==d[top]+1) 65 { 66 x=dfs(to,min(flow-ans,edge[i].w)); 67 edge[i].w-=x; 68 edge[i^1].w+=x; 69 if(edge[i].w) 70 cur[top] = i; 71 ans+=x; 72 if(ans==flow) 73 return flow; 74 } 75 } 76 if(ans==0) 77 d[top]=0; 78 return ans; 79 } 80 int dinic(int n) 81 { 82 int ans=0; 83 while(bfs()) 84 { 85 for(int i=0; i<=n; i++) 86 cur[i]=first[i]; 87 ans+=dfs(s,inf); 88 } 89 return ans; 90 } 91 int p[33]; 92 vector<int>v[33]; 93 94 int fa[33]; 95 int _find(int x) 96 { 97 while(x!=fa[x])x=x[fa]=x[fa][fa]; 98 return x; 99 } 100 void _union(int x,int y) 101 { 102 int p1=_find(x),p2=_find(y); 103 if(p1==p2)return; 104 fa[p1]=p2; 105 } 106 int main() 107 { 108 creat(); 109 int n,m,k; 110 scanf("%d%d%d",&n,&m,&k); 111 n+=2; 112 for(int i=1;i<=n;i++) 113 fa[i]=i; 114 s=0,t=1; 115 int st=n+n-1; 116 add_edge(s,st,k); 117 for(int i=1;i<=m;i++) 118 { 119 scanf("%d",p+i); 120 int r; 121 scanf("%d",&r); 122 while(r--) 123 { 124 int x; 125 scanf("%d",&x); 126 if(x==0) 127 x=n-1; 128 else if(x==-1) 129 x=n; 130 v[i].push_back(x); 131 if(v[i].size()>1) 132 _union(v[i][v[i].size()-2],v[i][v[i].size()-1]); 133 } 134 } 135 if(k!=0&&_find(n-1)!=_find(n)) 136 { 137 printf("0\n"); 138 return 0; 139 } 140 141 int now=1,sum=0; 142 while(sum!=k) 143 { 144 for(int i=1;i<=m;i++) 145 add_edge(n*now+v[i][(now-1)%v[i].size()],n*(now+1)+v[i][now%v[i].size()],p[i]); 146 for(int i=1;i<=n;i++) 147 add_edge(n*now+i,n*(now+1)+i,inf); 148 add_edge(n*(now+1)+n,t,inf); 149 sum+=dinic(n*(now+2)); 150 now++; 151 } 152 printf("%d\n",now-1); 153 return 0; 154 }
14、数字梯形问题
题目链接:https://www.oj.swust.edu.cn/problem/show/1751
建图方案,这个题就是套路拆点限制一下流量然后跑费用流就好了,具体的看看代码你们应该也能懂,把点拆成入点和出点
1 #include <cstdio> 2 #include <algorithm> 3 #include <cmath> 4 #include <cstring> 5 #include <iostream> 6 #include <ctime> 7 #include <map> 8 #include <queue> 9 #include <cstdlib> 10 #include <string> 11 #include <climits> 12 #include <set> 13 #include <vector> 14 using namespace std; 15 const int MAXN=5001; 16 const int MAXM=50001; 17 bool vis[MAXN]; 18 int dist[MAXN]; 19 int s,t,ans; 20 const int inf=1e9; 21 int first[MAXN],sign; 22 struct EDGE 23 { 24 int p,c,cc,next; 25 } edge[MAXM]; 26 27 void creat() 28 { 29 memset(first,-1,sizeof(first)); 30 sign=ans=0; 31 } 32 33 void add_edge(int from,int to,int c,int cost) 34 { 35 edge[sign].p=to; 36 edge[sign].c=c; 37 edge[sign].cc=cost; 38 edge[sign].next=first[from]; 39 first[from]=sign++; 40 edge[sign].p=from; 41 edge[sign].c=0; 42 edge[sign].cc=-cost; 43 edge[sign].next=first[to]; 44 first[to]=sign++; 45 } 46 47 inline bool spfa(int n,int s,int t) 48 { 49 memset(vis,0,sizeof vis); 50 for(int i=0; i<=n; i++)dist[i]=1e9; 51 dist[t]=0; 52 vis[t]=1; 53 deque<int>q; 54 q.push_back(t); 55 while(!q.empty()) 56 { 57 int now=q.front(); 58 q.pop_front(); 59 for(int k=first[now]; k>-1; k=edge[k].next)if(edge[k^1].c&&dist[edge[k].p]>dist[now]-edge[k].cc) 60 { 61 dist[edge[k].p]=dist[now]-edge[k].cc; 62 if(!vis[edge[k].p]) 63 { 64 vis[edge[k].p]=1; 65 if(!q.empty()&&dist[edge[k].p]<dist[q.front()])q.push_front(edge[k].p); 66 else q.push_back(edge[k].p); 67 } 68 } 69 vis[now]=0; 70 } 71 return dist[s]<1e9; 72 } 73 inline int dfs(int x,int low) 74 { 75 if(x==t) 76 { 77 vis[t]=1; 78 return low; 79 } 80 int used=0,a; 81 vis[x]=1; 82 for(int k=first[x]; k>-1; k=edge[k].next)if(!vis[edge[k].p]&&edge[k].c&&dist[x]-edge[k].cc==dist[edge[k].p]) 83 { 84 a=dfs(edge[k].p,min(edge[k].c,low-used)); 85 if(a)ans+=a*edge[k].cc,edge[k].c-=a,edge[k^1].c+=a,used+=a; 86 if(used==low)break; 87 } 88 return used; 89 } 90 inline int costflow(int n) 91 { 92 int flow=0; 93 while(spfa(n,s,t)) 94 { 95 vis[t]=1; 96 while(vis[t]) 97 { 98 memset(vis,0,sizeof vis); 99 flow+=dfs(s,1e9); 100 } 101 } 102 return flow; 103 } 104 int a[55][55]; 105 int id[55][55],wid[55][55]; 106 int main() 107 { 108 creat(); 109 int n,m; 110 scanf("%d%d",&m,&n); 111 s=0; 112 int cnt=1; 113 for(int i=1; i<=n; i++) 114 { 115 for(int j=1; j<=m+i-1; j++) 116 scanf("%d",&a[i][j]),id[i][j]=cnt++,wid[i][j]=cnt++; 117 } 118 t=cnt; 119 for(int i=1; i<=n; i++) 120 { 121 for(int j=1; j<=m+i-1; j++) 122 { 123 add_edge(id[i][j],wid[i][j],1,-a[i][j]); 124 if(i!=n) 125 { 126 add_edge(wid[i][j],id[i+1][j],1,0); 127 add_edge(wid[i][j],id[i+1][j+1],1,0); 128 } 129 } 130 } 131 for(int i=1; i<=m; i++) 132 add_edge(s,id[1][i],1,0); 133 for(int i=1; i<=n+m-1; i++) 134 add_edge(wid[n][i],t,1,0); 135 costflow(cnt); 136 printf("%d\n",-ans); 137 creat(); 138 for(int i=1; i<=n; i++) 139 { 140 for(int j=1; j<=m+i-1; j++) 141 { 142 add_edge(id[i][j],wid[i][j],inf,-a[i][j]); 143 if(i!=n) 144 { 145 add_edge(wid[i][j],id[i+1][j],1,0); 146 add_edge(wid[i][j],id[i+1][j+1],1,0); 147 } 148 } 149 } 150 for(int i=1; i<=m; i++) 151 add_edge(s,id[1][i],1,0); 152 for(int i=1; i<=n+m-1; i++) 153 add_edge(wid[n][i],t,inf,0); 154 costflow(cnt); 155 printf("%d\n",-ans); 156 157 creat(); 158 for(int i=1; i<=n; i++) 159 { 160 for(int j=1; j<=m+i-1; j++) 161 { 162 add_edge(id[i][j],wid[i][j],inf,-a[i][j]); 163 if(i!=n) 164 { 165 add_edge(wid[i][j],id[i+1][j],inf,0); 166 add_edge(wid[i][j],id[i+1][j+1],inf,0); 167 } 168 } 169 } 170 for(int i=1; i<=m; i++) 171 add_edge(s,id[1][i],1,0); 172 for(int i=1; i<=n+m-1; i++) 173 add_edge(wid[n][i],t,inf,0); 174 costflow(cnt); 175 printf("%d\n",-ans); 176 return 0; 177 }
21、最长k可重区间集问题
题目连接:https://www.oj.swust.edu.cn/problem/show/1756或者https://loj.ac/problem/6014
一年后我又开始了,,不过这次只想补几个经典题==
建图方案:把区间坐标离散化,然后每个点向下一个点连一条边,从起点往1连一条边,对于每个i,从l[i]->r[i]连一条边。这个建图是什么意思呢?就是说,你有k次选择机会,每次会选择一些不相交的区间,不选的话就直接不消耗费用走到下一个点,选的话就从l->r点。建图就是这样。
注意:poweroj上的数据有问题,会出现l[i]>r[i]的情况,但是你必须得当作不知道来建图2333,不然会wa8。
1 #include <cstdio> 2 #include <algorithm> 3 #include <queue> 4 #include <cstring> 5 #include <vector> 6 using namespace std; 7 const int MAXN=2111; 8 const int MAXM=20001; 9 bool vis[MAXN];int dist[MAXN]; 10 int s,t,ans; 11 12 int first[MAXN],sign; 13 struct EDGE 14 { 15 int p,c,cc,next; 16 }edge[MAXM]; 17 18 void creat() 19 { 20 memset(first,-1,sizeof(first)); 21 sign=ans=0; 22 } 23 24 void add_edge(int from,int to,int c,int cost) 25 { 26 edge[sign].p=to; 27 edge[sign].c=c; 28 edge[sign].cc=cost; 29 edge[sign].next=first[from]; 30 first[from]=sign++; 31 edge[sign].p=from; 32 edge[sign].c=0; 33 edge[sign].cc=-cost; 34 edge[sign].next=first[to]; 35 first[to]=sign++; 36 } 37 38 inline bool spfa(int n,int s,int t){ 39 memset(vis,0,sizeof vis); 40 for(int i=0;i<=n;i++)dist[i]=1e9;dist[t]=0;vis[t]=1; 41 deque<int>q;q.push_back(t); 42 while(!q.empty()){ 43 int now=q.front();q.pop_front(); 44 for(int k=first[now];k>-1;k=edge[k].next)if(edge[k^1].c&&dist[edge[k].p]>dist[now]-edge[k].cc){ 45 dist[edge[k].p]=dist[now]-edge[k].cc; 46 if(!vis[edge[k].p]){ 47 vis[edge[k].p]=1; 48 if(!q.empty()&&dist[edge[k].p]<dist[q.front()])q.push_front(edge[k].p);else q.push_back(edge[k].p); 49 } 50 } 51 vis[now]=0; 52 } 53 return dist[s]<1e9; 54 } 55 inline int dfs(int x,int low){ 56 if(x==t){vis[t]=1;return low;} 57 int used=0,a;vis[x]=1; 58 for(int k=first[x];k>-1;k=edge[k].next)if(!vis[edge[k].p]&&edge[k].c&&dist[x]-edge[k].cc==dist[edge[k].p]){ 59 a=dfs(edge[k].p,min(edge[k].c,low-used)); 60 if(a)ans+=a*edge[k].cc,edge[k].c-=a,edge[k^1].c+=a,used+=a; 61 if(used==low)break; 62 } 63 return used; 64 } 65 inline int costflow(int n){ 66 int flow=0; 67 while(spfa(n,s,t)){ 68 vis[t]=1; 69 while(vis[t]){ 70 memset(vis,0,sizeof vis); 71 flow+=dfs(s,1e9); 72 } 73 } 74 return flow; 75 } 76 vector<int>v; 77 int getid(int x) { 78 return lower_bound(v.begin(),v.end(),x)-v.begin()+1; 79 } 80 int l[MAXN],r[MAXN]; 81 int main() 82 { 83 creat(); 84 int n,k; 85 scanf("%d%d",&n,&k); 86 for(int i=1;i<=n;i++){ 87 scanf("%d%d",l+i,r+i); 88 if(l[i]>r[i])swap(l[i],r[i]); 89 v.push_back(l[i]),v.push_back(r[i]); 90 } 91 sort(v.begin(),v.end()); 92 v.erase(unique(v.begin(),v.end()),v.end()); 93 int tot=v.size(); 94 s=0;t=tot+1; 95 add_edge(s,1,k,0); 96 for(int i=1;i<tot;i++) 97 add_edge(i,i+1,k,0); 98 add_edge(tot,t,k,0); 99 for(int i=1;i<=n;i++) { 100 add_edge(getid(l[i]),getid(r[i]),1,l[i]-r[i]); 101 } 102 costflow(t); 103 printf("%d\n",-ans); 104 return 0; 105 }