浅谈费用流

\(SAP\)

\(\mathcal{No.1} 方法及证明\)

\(SSP算法是一个贪心的算法。它的思路是每次寻找单位费用最小的增广路进行增广,直到图上不存在增广路为止。\)

\(如果图上存在单位费用为负的圈,SSP 算法正确无法求出该网络的最小费用最大流。此时需要先使用消圈算法消去图上的负圈。\)

\(同dinic一样,这里的增广路的长度是单调不递减的\)

\(这样找能够保证最大流,但需证明一定是最小费用\)

\(引理:当流量为i时,且图中不存在负环,那么此时是流量为i时最小费用\)

\(考虑对于流量为i的f_i及其i+1的f_{i+1}\)

\(假设f_i为最小费用并且f_{i+1}为f_i用最短路增广出的流\)

\(假设存在f_{i+1}'为f_i增广出的流比f_i更小\)

\(\therefore f_{i+1}'的增广路径小于f_{i+1}\)

\(\therefore f_{i+1}'的增广路径中有负环,这与其矛盾\)

\(回到其最开始的证明,即最短路增广的正确性\)

\(设f_i为第i次增广的流\)

\(假设存在f_i',使得|f'_i|=|f_i|且f'_i的花费小于f_i的花费\)

\(由于|f_{i-1}|<|f'_{i}|,因此一定存在增广路于f_{i-1}到f'_{i}\)

\(又由于f'_i的花费小于f_i的花费,f_i是最短路增广\)

\(所以f'_i的增广路径存在负环,于引理矛盾,因此假设不成立\)

\(\mathcal{No.2} 例题\)

P4134 [BJOI2012]连连看

\(给每一个满足条件的x,y连边,可以发现在1000以内是一个二分图,跑最大费用流即可\)

Show Code
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 6005;
const int MAXM = 50005;
int n, m;
int x, y, z, c;
struct Edge {
    int to, nex;
    int val;
    int cost;
} edge[MAXM * 2];
int cnt = 1;
int head[MAXN];
void Add(int u, int v, int w, int cost) {
    edge[++cnt].to = v;
    edge[cnt].nex = head[u];
    edge[cnt].val = w;
    edge[cnt].cost = -cost;
    head[u] = cnt;
}

int dis[MAXN];
int vis[MAXN];
int arc[MAXN];
int s, t;
int Energy[MAXN];
bool spfa() {
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    queue<int>q;

    q.push(s);
    dis[s] = 0;
    vis[s] = 1;

    while (q.size()) {
        int temp = q.front();
        q.pop();
        vis[temp] = 0;
        arc[temp] = head[temp];

        for (int i = head[temp]; i; i = edge[i].nex) {
            int v = edge[i].to;
            int w = edge[i].cost;

            if (!edge[i].val) {
                continue;
            }

            if (dis[v] > dis[temp] + w) {
                dis[v] = dis[temp] + w;

                if (!vis[v]) {
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
    }

    memset(vis, 0, sizeof(vis));

    if (dis[t] == 0x3f3f3f3f) {
        return 0;
    }

    return 1;

}
int mincost = 0;
int maxflow = 0;
int dfs(int x, int flow) {
    if (x == t || flow == 0) {
        return flow;
    }

    int used = 0;
    vis[x] = 1;

    for (int i = arc[x]; i; i = edge[i].nex) {

        int v = edge[i].to;
        int w = edge[i].cost;
        if (vis[v]) {
            continue;
        }

        if (!edge[i].val) {
            continue;
        }

        if (dis[x] + w == dis[v]) {
            arc[x] = i;
            int nowu = dfs(v, min(flow - used, edge[i].val));
            used += nowu;

            edge[i].val -= nowu;
            edge[i ^ 1].val += nowu;
            mincost += nowu * w;

            if (used == flow) {
                break;
            }
        }
    }

    vis[x] = 0;
    return used;
}
struct node{
	int u,val;
	bool operator<(const node x)const{
		return val>x.val;
	}
};
bool dijkstra()
{
	
	for(int i=0;i<=n+50;i++)
	{
		dis[i]=0x3f3f3f3f;
		vis[i]=0;
		 
	}
	dis[s]=0;
	arc[s]=head[s];
	priority_queue<node>q;
	node gsg;
	gsg.u=s;
	gsg.val=0;
	q.push(gsg);
	while(q.size())
	{
		node temp=q.top();
		q.pop();
		if(vis[temp.u])
		{
			continue;
		}
		vis[temp.u]=1;
		arc[temp.u]=head[temp.u];
		for(int i=head[temp.u];i;i=edge[i].nex)
		{
			int v=edge[i].to;
			int w=Energy[temp.u]-Energy[v]+edge[i].cost;//保证无负权 
			if(!edge[i].val)
			{
				continue;
			}
			if(dis[v]>dis[temp.u]+w)
			{
				dis[v]=dis[temp.u]+w;
				node gaf;
				gaf.val=dis[v];
				gaf.u=v;
				q.push(gaf);
			}
		}
	}
	for(int i = 0; i <= n+50; i++)
	{
 		dis[i]=dis[i]-Energy[s]+Energy[i];//势能函数的更新要累加当前的 
    }
    for(int i = 0; i <= n+50; i++)
    {
    	Energy[i] = dis[i];
	}
        

	memset(vis,0,sizeof(vis));
	if(dis[t]>=0x3f3f3f3f)
	{
		return 0;
	}
	return 1;
 } 
pair<int, int>dinic() {
	mincost=0;
	maxflow=0;
    if(!spfa())
    {
    	return make_pair(maxflow,mincost);
	}
	for(int i=0;i<=n+50;i++)
	{
		Energy[i]=dis[i];
	}//这里清空要注意范围 
	while(dijkstra())
	{
	//	printf("fiuck");
		maxflow+=dfs(s,INF);
		
	}
	return make_pair(maxflow,mincost);
	
}
int clor[MAXN];
vector<int>g[MAXN];
bool is2(int x)
{
	if(int(sqrt(x))==sqrt(x))
	{
		return 1;
	}
	return 0;
 } 
 int gcd(int a,int b){
 	if(!b)
 	{
 		return a;
	 }
	 return gcd(b,a%b);
 }
 void Dfs(int x,int clo)
 {
 	if(clor[x])
 	{
 		return;
	 }
	 clor[x]=clo;
	 for(int i=0;i<g[x].size();i++)
	 {
	 	int v=g[x][i];
	 	Dfs(v,clo==1?2:1);
	 }
 }
int main() {
	scanf("%d %d",&m,&n);
	for(int i=m;i<=n;i++)
	{
		for(int j=m+1;j<=n;j++)
		{
			if(is2(j*j-i*i))
			{
				//printf("%d %d\n",i,j);
				if(gcd(i,sqrt(j*j-i*i))==1)
				{
					g[i].push_back(j);
					g[j].push_back(i); 
					
				}
			}
		}	
	}    
	for(int i=m;i<=n;i++)
	{
		if(!clor[i])
		{
			Dfs(i,1);
		}
	}
	for(int i=m;i<=n;i++)
	{
		if(clor[i]==1)
		{
			Add(0,i-m+1,1,0);
			Add(i-m+1,0,0,0);
			for(int j=0;j<g[i].size();j++)
			{
				int v=g[i][j];
				Add(i-m+1,v-m+1,1,i+v);
				Add(v-m+1,i-m+1,0,-(i+v));
			}
		}
		else
		{
			Add(i-m+1,n-m+2,1,0);
			Add(n-m+2,i-m+1,0,0);
		}
	}
	s=0;
	t=n-m+2;
	pair<int,int>pi=dinic();
	printf("%d %d",pi.first,pi.second*-1);
}

Show Code

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 4005;
const int MAXM = 50005;
int n, m;
int x, y, z, c;
struct Edge {
    int to, nex;
    int val;
    int cost;
} edge[MAXM * 2];
int cnt = 1;
int head[MAXN];
void Add(int u, int v, int w, int cost) {
    edge[++cnt].to = v;
    edge[cnt].nex = head[u];
    edge[cnt].val = w;
    edge[cnt].cost = cost;
    head[u] = cnt;
}

int dis[MAXN];
int vis[MAXN];
int arc[MAXN];
int s, t;

bool spfa() {
    for(int i=s;i<=t;i++)
    {
    	dis[i]=-0x3f3f3f3f;
	}
    memset(vis, 0, sizeof(vis));
    queue<int>q;

    q.push(s);
    dis[s] = 0;
    vis[s] = 1;

    while (q.size()) {
        int temp = q.front();
        q.pop();
        vis[temp] = 0;
        arc[temp] = head[temp];

        for (int i = head[temp]; i; i = edge[i].nex) {
            int v = edge[i].to;
            int w = edge[i].cost;

            if (!edge[i].val) {
                continue;
            }

            if (dis[v] < dis[temp] + w) {
                dis[v] = dis[temp] + w;

                if (!vis[v]) {
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
    }

    memset(vis, 0, sizeof(vis));

    if (dis[t] == -0x3f3f3f3f) {
        return 0;
    }

    return 1;

}
int mincost = 0;
int maxflow = 0;
int dfs(int x, int flow) {
    if (x == t || flow == 0) {
        return flow;
    }

    int used = 0;
    vis[x] = 1;

    for (int i = arc[x]; i; i = edge[i].nex) {

        int v = edge[i].to;
        int w = edge[i].cost;

        if (vis[v]) {
            continue;
        }

        if (!edge[i].val) {
            continue;
        }

        if (dis[x] + w == dis[v]) {
            arc[x] = i;
            int nowu = dfs(v, min(flow - used, edge[i].val));
            used += nowu;

            edge[i].val -= nowu;
            edge[i ^ 1].val += nowu;
            mincost += nowu * w;

            if (used == flow) {
                break;
            }
        }
    }

    vis[x] = 0;
    return used;
}
pair<int, int>dinic() {
    maxflow = 0;
    mincost = 0;
    int flow = 0;

    while (spfa()) {
        maxflow += dfs(s, INF);
    }

    return make_pair(maxflow, mincost);
}
int clor[MAXN];
vector<int>g[MAXN];
bool is2(int x)
{
	if(int(sqrt(x))==sqrt(x))
	{
		return 1;
	}
	return 0;
 } 
 int gcd(int a,int b){
 	if(!b)
 	{
 		return a;
	 }
	 return gcd(b,a%b);
 }
 void Dfs(int x,int clo)
 {
 	if(clor[x])
 	{
 		return;
	 }
	 clor[x]=clo;
	 for(int i=0;i<g[x].size();i++)
	 {
	 	int v=g[x][i];
	 	Dfs(v,clo==1?2:1);
	 }
 }
int main() {
	scanf("%d %d",&m,&n);
	for(int i=m;i<=n;i++)
	{
		for(int j=m+1;j<=n;j++)
		{
			if(is2(j*j-i*i))
			{
				//printf("%d %d\n",i,j);
				if(gcd(i,sqrt(j*j-i*i))==1)
				{
					g[i].push_back(j);
					g[j].push_back(i); 
					
				}
			}
		}	
	}    
	for(int i=m;i<=n;i++)
	{
		if(!clor[i])
		{
			Dfs(i,1);
		}
	}
	for(int i=m;i<=n;i++)
	{
		if(clor[i]==1)
		{
			Add(0,i-m+1,1,0);
			Add(i-m+1,0,0,0);
			for(int j=0;j<g[i].size();j++)
			{
				int v=g[i][j];
				Add(i-m+1,v-m+1,1,i+v);
				Add(v-m+1,i-m+1,0,-(i+v));
			}
		}
		else
		{
			Add(i-m+1,n-m+2,1,0);
			Add(n-m+2,i-m+1,0,0);
		}
	}
	s=0;
	t=n-m+2;
	pair<int,int>pi=dinic();
	printf("%d %d",pi.first,pi.second);
}

线性规划简述

对于问题

\[\left\{ \begin{array}{**lr**} \max \sum\limits_{j=1}^n c_jx_j \\ \forall i \in[1,m],\sum\limits_{j=1}^na_{i,j}x_{j}\le b_i \\ x_j\ge 0 \end{array} \right. \]

其对偶问题为

\[\left\{ \begin{array}{**lr**} \min \sum\limits_{j=1}^m b_jy_j \\ \forall i \in[1,n],\sum\limits_{j=1}^ma_{j,i}y_{j}\ge c_i \\ y_j\ge 0 \end{array} \right. \]

这两问题是等价的,强对偶定理(证明不会

考虑描述成矩阵的形式(忽略\(\ge0\)的条件

\[\left\{ \begin{array}{**lr**} \min Cx \\ Ax\ge B \end{array} \right. \]

\[\left\{ \begin{array}{**lr**} \max B^Ty \\ A^Ty\le C^T \end{array} \right. \]

线性规划与费用流

考虑一般的费用流,设\(b_u\)为流入\(-\)流出

\[\left\{ \begin{array}{**lr**} \max cost_i flow_i \\ flow_i\ge 0\\ flow_i\le cap_i \\ \sum\limits_{v}f_{v,u}-\sum\limits_{v}f_{u,v}=b_u \end{array} \right. \]

注意到\(x_i\le z_i,\sum x_i=\sum\limits z_i\)等价于\(x_i=z_i\)

则可改写为

\[\left\{ \begin{array}{**lr**} \max cost_i flow_i \\ flow_i\ge 0\\ flow_i\le cap_i \\ \sum\limits_{v}f_{v,u}-\sum\limits_{v}f_{u,v}\le b_u \end{array} \right. \]

考虑构造一个\(n+m\)行,\(m\)列的矩阵\(A\),其中前\(n\)行中的\(i\),\(val_{i,j}\)\(e_{j,u}=i\)时为\(-1\),\(e_{j,v}=i\)时为\(1\),后\(m\)行为单位矩阵,\(cost_i\)构成的矩阵为\(C\),\(b_u\)\(cap_i\)构成的为\(B\)

考虑对偶

\[\left\{ \begin{array}{**lr**} \min B^Tx \\ A^Tx\ge C^T \end{array} \right. \]

\(x\)

\[\left [ \begin{matrix} \varphi_1 \\ \varphi_2\\ ... \\ \varphi_n \end{matrix} \right ] \\ \left [ \begin{matrix} y1 \\ y2\\ ... \\ y_m \end{matrix} \right ] \]

则其等价于

\[\left\{ \begin{array}{**lr**} \max b_u\varphi_u+cap_iy_i \\ -\varphi_u+\varphi_v +y_i\le cost_i \end{array} \right. \]

注意到取最大时\(y_i\le cost_i+\varphi_u-\varphi_v\)

则原式等价于\(\min \sum b_u\varphi_u+\sum\limits cap_i \max(0,cost_i+\varphi_u-\varphi_v)\)

看到上面这种可以直接转化成其对偶问题

另外说一嘴,前面那个东西怎么建图

对于\(b_u>0\)的,我们虚拟原点\(S\)连一条容量为\(b_u\)边权的\(0\)的边

对于\(b_u<0\)的,我们将它向\(T\)连一条\(-b_u\)边权为\(0\)的边

剩下的我们跑有负环的最小费用可行流

大概是把负边先流满然后跑上下界最小费用可行流

对了,还有就是可能你会觉得有些环跑不到

不过这里的定义是环上的流量可以随便流,不一定要流到

[ZJOI2013] 防守战线

\[\left\{ \begin{array}{**lr**} \min(sum_i-sum_{i-1})c_i \\ \forall i\in[1,m],sum_{R_i}-sum_{L_i}\ge D_i \\ sum_i-sum_{i-1}\ge 0 \end{array} \right. \]

神奇的可以套用上面的模型

\(\varphi_i=sum_i\),于是其等价于\(\min \sum (sum_i-sum_{i-1})c_i=\min \sum sum_i(c_i-c_{i+1})\)

我们可以将违反条件看作是一种惩罚,即

\(min\sum\limits (c_{i}-c_i{+1})\varphi_i+\sum\limits_{}\inf \times \max(0,D_i-\varphi_{R_i}+\varphi_{L_i})+\sum\limits_{}\inf \times \max(0,-\varphi_{i}+\varphi_{i-1})\)

跑对应的最大费用流即可

[NOI2008] 志愿者招募

和上面一题很像

\[\left\{ \begin{array}{**lr**} \min c_ix_i\\ \forall i\in[1,n],\sum\limits(x_j)[l_j\le i\le r_j]\ge a_i \end{array} \right. \]

我们考虑对下面的不等式做一次差分,这样每个\(x_j\)只会出现两次且一正一负

考虑其对偶问题

\[\left\{ \begin{array}{**lr**} \max a_iy_i\\ \forall i\in[1,m],\sum\limits_{j=l_i}^{r_i} y_j\le c_i \end{array} \right. \]

然后就一模一样了

\(max \sum\limits (c_{i}-c_{i+1})\varphi_i+\sum\limits_{}-\inf \times \max(0,-D_i+\varphi_{R_i}-\varphi_{L_i})+\sum\limits_{}-\inf \times \max(0,-\varphi_{i}+\varphi_{i-1})\)

似乎还不太一样

不过这里全部对答案取负就差不多了

注意\(b_u\)也是反的

「BZOJ3118」Orz the MST

有个贪心,树边一定减小,其余边一定增大

还是先列出式子

\[\left\{ \begin{array}{**lr**} \min A_ix_i \\ x_i+x_j\ge v_j-v_i \end{array} \right. \]

\(\min A_ix_i+\sum\limits \inf\max(0,v_j-v_i-x_i-x_j)\)

符号不同似乎不行

先对偶一下

\[\left\{ \begin{array}{**lr**} \max (v_j-v_i)y_p\\ \forall i\in[1,m],\sum\limits_{j=p}^My_p[i\in p]\le A_i \end{array} \right. \]

把边看作点,每条限制看作连边

然后就可以直接建图即可

posted @ 2022-01-06 21:29  kid_magic  阅读(334)  评论(0编辑  收藏  举报