网络流。
原理:
1、把所有的顶点bfs,根据拓扑序列标记层数。
2、dfs走一遍所有可走的子节点,同时把流量记下来。每次纪录流量的时候,正向边加上对应流量,反向边减去对应边,
放一下模板
int N,M;
struct pp
{
int v,next,w,re;
}pra[SIZE_M];
int e,head[SIZE_N];
void init()
{
e = 0;
memset(head,-1,sizeof(head[0]) * (N + 10) );
}
void addedge(int x,int y,int z1,int z2)
{
pra[e].re = e+1;
pra[e].v = y;
pra[e].w = z1;
pra[e].next = head[x];
head[x] = e++;
pra[e].re = e-1;
pra[e].v = x;
pra[e].w = z2;
pra[e].next = head[y];
head[y] = e++;
}
int level[SIZE_N];
void bfs(int x)
{
memset(level, -1, sizeof(level[0])* (N + 10));
level[x] = 0;
queue<int> que;
que.push(x);
while (!que.empty()){
int ver = que.front();
que.pop();
for (int i = head[ver]; i != -1; i = pra[i].next){
int u = pra[i].v;
if (pra[i].w > 0 && level[u] < 0){
level[u] = level[ver]+1;
que.push(u);
}
}
}
}
int dfs(int s,int t,int cap)//一次dfs把当前经过的都算了
{
if (s == t) return cap;//若当前顶点就是终点
int addc = 0;
int ver = s;
for (int i = head[ver]; i != -1 && cap - addc > 0; i = pra[i].next){
int u = pra[i].v;
if (pra[i].w > 0 && level[ver] < level[u]){//如果可以增广而且当前点在父节点的下一层
int f = dfs(u,t,min(cap-addc,pra[i].w));
if (f > 0){
pra[i].w -= f;
addc += f;//一次dfs把当前经过的子节点的最大流都算了
pra[pra[i].re].w += f;
}
}
}
if (!addc) level[s] = -1;//如果当前节点不存在增广路,那么其他节点要是指向这条路的话
return addc;
}
int max_flow(int s,int t)
{
int flow = 0;
while (1){
bfs(s);//bfs将当前的顶点按照拓扑序列排列
if (level[t] < 0)
return flow;//如果bfs失败,说明没有路了
int temp;
while ( (temp = dfs(s,t,INF)) > 0){
flow += temp;
}
}
}
先放代码:
</pre><pre name="code" class="cpp">/*题意:有a核和b核,然后有a核的处理时间ai,b核的处理时间bi。
如果某两个特定任务不在同一个核上进行那么有附加时间ci;
解答:有一个源点,一个汇点,和其余的n个点。
源点连向其余的点,边权为ai,其余的点连向汇点,边权为bi。
建图原因:
首先,如果没有条件2,那么肯定是选ab中最小的。单条路径的最大流就是当前路径中能经过的最大流量,也就是最细管道的流量
增加了条件2,那么如果要使用这些增加的ci值的那条路径, 那么,肯定序号i和序号j使用的是同个核。设i,j使用a。
*/
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<queue>
#define INF 0x3f3f3f3f
#define SIZE_N 88005
#define SIZE_M 880005//开500005就会RE
using namespace std;
int N,M;
struct pp
{
int v,next,w,re;
}pra[SIZE_M];
int e,head[SIZE_N];
void init()
{
e = 0;
memset(head,-1,sizeof(head[0]) * (N + 10) );
}
void addedge(int x,int y,int z1,int z2)
{
pra[e].re = e+1;
pra[e].v = y;
pra[e].w = z1;
pra[e].next = head[x];
head[x] = e++;
pra[e].re = e-1;
pra[e].v = x;
pra[e].w = z2;
pra[e].next = head[y];
head[y] = e++;
}
int level[SIZE_N];
void bfs(int x)
{
memset(level, -1, sizeof(level[0])* (N + 10));
level[x] = 0;
queue<int> que;
que.push(x);
while (!que.empty()){
int ver = que.front();
que.pop();
for (int i = head[ver]; i != -1; i = pra[i].next){
int u = pra[i].v;
if (pra[i].w > 0 && level[u] < 0){
level[u] = level[ver]+1;
que.push(u);
}
}
}
}
int dfs(int s,int t,int cap)//一次dfs把当前经过的都算了
{
if (s == t) return cap;//若当前顶点就是终点
int addc = 0;
int ver = s;
for (int i = head[ver]; i != -1 && cap - addc > 0; i = pra[i].next){
int u = pra[i].v;
if (pra[i].w > 0 && level[ver] < level[u]){//如果可以增广而且当前点在父节点的下一层
int f = dfs(u,t,min(cap-addc,pra[i].w));
if (f > 0){
pra[i].w -= f;
addc += f;//一次dfs把当前经过的子节点的最大流都算了
pra[pra[i].re].w += f;
}
}
}
if (!addc) level[s] = -1;//如果当前节点不存在增广路,那么其他节点要是指向这条路的话
return addc;
}
int max_flow(int s,int t)
{
int flow = 0;
while (1){
bfs(s);//bfs将当前的顶点按照拓扑序列排列
if (level[t] < 0)
return flow;//如果bfs失败,说明没有路了
int temp;
while ( (temp = dfs(s,t,INF)) > 0){
flow += temp;
}
}
}
int main()
{
while (~scanf("%d %d",&N,&M)){
init();
for (int i = 1; i <= N; i++){
int tempa,tempb;
scanf("%d %d",&tempa,&tempb);
addedge(0,i,tempa,0);
addedge(i,N+1, tempb,0);
}
for (int i = 1; i <= M; i++){
int temp1,temp2,temp3;
scanf("%d %d %d",&temp1,&temp2,&temp3);
addedge(temp1,temp2,temp3,temp3);
}
printf("%d\n",max_flow(0,N+1));
}
return 0;
}
/*
3 1
1 10
2 10
10 3
2 3 1000
3 1
1 10
2 10
10 3
2 3 1000
*/