浅谈费用流
\(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);
}
线性规划简述
对于问题
其对偶问题为
这两问题是等价的,强对偶定理(证明不会
考虑描述成矩阵的形式(忽略\(\ge0\)的条件
即
和
线性规划与费用流
考虑一般的费用流,设\(b_u\)为流入\(-\)流出
注意到\(x_i\le z_i,\sum x_i=\sum\limits z_i\)等价于\(x_i=z_i\)
则可改写为
考虑构造一个\(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\)
考虑对偶
设\(x\)为
则其等价于
注意到取最大时\(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] 防守战线
神奇的可以套用上面的模型
令\(\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] 志愿者招募
和上面一题很像
我们考虑对下面的不等式做一次差分,这样每个\(x_j\)只会出现两次且一正一负
考虑其对偶问题
然后就一模一样了
\(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
有个贪心,树边一定减小,其余边一定增大
还是先列出式子
\(\min A_ix_i+\sum\limits \inf\max(0,v_j-v_i-x_i-x_j)\)
符号不同似乎不行
先对偶一下
把边看作点,每条限制看作连边
然后就可以直接建图即可