1. 程式人生 > >[學習筆記]費用流

[學習筆記]費用流

一些內容在另一篇部落格

[學習筆記]網路

 

 

有的時候要保證最大的情況下,費用盡可能優。

就要用費用流了。

目前所涉及的費用流,都是在最大流的前提下

 

所以,當題目可以轉化成,在保證。。。的情況下,最優化。。。

也許就可以嘗試費用流了。

 

(同樣意味著選擇,

最小割沒有什麼最大流的前提,可以沒有什麼限制地,割掉一些邊即可。

但是,每條邊必須割掉,不能“割一部分”,對於一些可以剩下的模型就捉襟見肘了。

 

用EK。因為dinic不能保證流出來的是最優代價的。

至於為什麼每次貪心選擇最優的路徑,最後就是最優的,可能的原因是,因為有反邊,所以自帶反悔自動機功能。貪心沒有問題。

可以延伸出來一個結論:

最短路費用流,當前費用是所有能夠到達當前總流量的所有費用中最優的一個。

(偽證:

因為貪心成立。還沒有聽說過哪個貪心不是區域性最優解,卻是全域性最優解的2333

 

例題:

方格取數、晨跑。

拆點費用流即可。

 

修車,美食節

拆點費用流。

邊權設計:

反過來考慮每個人站在某個位置,對後面的人等待時間產生的影響。

第j個階段,意味著後面還有j個。這樣,貢獻就只和自己有關係了。

必要的時候動態加邊。

 

[SDOI2016]數字配對

倍數關係,這個倍數關係還是質數。

那麼,這兩個數的質因子一定相差一,差的那個質因子就是這個質數。

所以,把數按照質因子個數奇偶性分成左右兩類

左部點右部點自己之間不會有配對。

左部右部點之間,如果成倍數關係,並且一個是另一個質因子個數+1,那麼連邊流inf,費c1*c2。

左部點和S,連流b費0

右部點和T,連流b費0

這樣,每個流就是一個配對。

 

至於

“在獲得的價值總和不小於 0 的前提下,求最多進行多少次配對。”

費用流的特點是,

“可以保證當前的費用永遠是當前流量下最優的”

所以,如果當前總費用<0

那麼就可以停止了。因為之後,流量更大的話,費用也不可能更高。

#include<bits/stdc++.h>
#define
reg register int #define il inline #define numb (ch^'0') using namespace std; typedef long long ll; il void rd(int &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } namespace Miracle{ const int N=200*2+5; const int M=200*200+200+200; const int U=31622+20; const int inf=0x3f3f3f3f; int n,m,s,t; struct node{ int nxt,to; int w; ll v; }e[2*M]; int hd[N],cnt=1; void add(int x,int y,int z,ll c){ e[++cnt].nxt=hd[x]; e[cnt].to=y; e[cnt].w=z; e[cnt].v=c; hd[x]=cnt; } struct po{ int a,b,c; int cnt; bool friend operator <(po a,po b){ return a.a<b.a; } }a[N]; int pri[M],tot; bool vis[U]; void sieve(){ vis[1]=1; for(reg i=2;i<=U-10;++i){ if(!vis[i]){ pri[++tot]=i; } for(reg j=1;j<=tot;++j){ if((ll)pri[j]*i>U-10) break; vis[pri[j]*i]=1; if(i%pri[j]==0) break; } } } int che(int x){ int ret=0; for(reg i=1;i<=tot&&pri[i]*pri[i]<=x;++i){ if(x%pri[i]==0){ while(x%pri[i]==0) x/=pri[i],++ret; } } if(x!=1) ++ret; return ret; } ll d[N]; int pre[N]; int incf[N]; queue<int>q; ll now,flow; bool in[N]; bool spfa(){ memset(d,0xcf,sizeof d); while(!q.empty()) q.pop(); d[s]=0; q.push(s); pre[s]=0;incf[s]=inf; while(!q.empty()){ int x=q.front();q.pop(); in[x]=0; // cout<<"x "<<x<<endl; for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; // cout<<" yy "<<y<<endl; if(e[i].w){ if(d[y]<d[x]+e[i].v){ d[y]=d[x]+e[i].v; pre[y]=i; incf[y]=min(incf[x],e[i].w); if(!in[y]) { in[y]=1; q.push(y); } } } } } // cout<<" spfa "<<d[t]<<endl; if(d[t]==-3472328296227680305ll) return false; return true; } bool upda(){ //cout<<" d[t] "<<d[t]<<endl; if(now+d[t]*incf[t]<0){ flow+=now/abs(d[t]); return false; } int x=t; while(pre[x]){ //cout<<"upda "<<x<<endl; e[pre[x]].w-=incf[t]; e[pre[x]^1].w+=incf[t]; x=e[pre[x]^1].to; } flow+=incf[t]; now+=d[t]*incf[t]; //cout<<" now "<<now<<" "<<flow<<endl; return true; } int le[N],ri[N]; int lc,rc; int main(){ scanf("%d",&n); for(reg i=1;i<=n;++i){ rd(a[i].a); }for(reg i=1;i<=n;++i) rd(a[i].b); for(reg i=1;i<=n;++i) rd(a[i].c); sieve(); for(reg i=1;i<=n;++i){ a[i].cnt=che(a[i].a); if(a[i].cnt%2==0){ le[++lc]=i; }else ri[++rc]=i; } for(reg i=1;i<=lc;++i){ int id=le[i]; for(reg j=1;j<=rc;++j){ int to=ri[j]; if(abs(a[id].cnt-a[to].cnt)==1){ if(a[id].a%a[to].a==0||a[to].a%a[id].a==0){ add(id,to,inf,(ll)a[id].c*a[to].c); add(to,id,0,-(ll)a[id].c*a[to].c); } } } } s=n+1,t=n+2; for(reg i=1;i<=lc;++i){ int id=le[i]; add(s,id,a[id].b,0); add(id,s,0,0); } for(reg i=1;i<=rc;++i){ int to=ri[i]; add(to,t,a[to].b,0); add(t,to,0,0); } // cout<<" cnt "<<cnt<<endl; while(spfa()){ // cout<<" xx "<<endl; bool fl=upda(); if(!fl) break; } printf("%lld",flow); return 0; } } int main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2018/11/27 15:59:23 */
數字配對

 

[SDOI2013]費用流

看似費用流。

其實發現,Bob一定選擇最大的流量*p

所以,Alice要使得所選擇的最大流的最大流量最小。

二分判斷即可。

注意,邊最大流量不一定是整數。因為可以均攤成小數變得更低。

eps 1e-5即可。

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int N=103;
const int M=1000+3;
const int inf=1023333333.00;
const double eps=1e-8;
int n,m,p,s,t;
struct bian{
    int x,y,c;
}b[M];
struct node{
    int nxt,to;
    double w;
}e[2*M];
int hd[N],cnt=1;
void add(int x,int y,double z){
    //cout<<" x y z "<<x<<" "<<y<<" "<<z<<endl;
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    e[cnt].w=z;
    hd[x]=cnt;
}
int d[N];
double dfs(int x,double flow){
    if(x==t) return flow;
    double res=flow;
    for(reg i=hd[x];i&&fabs(res)>=eps;i=e[i].nxt){
        int y=e[i].to;
        if(e[i].w&&d[y]==d[x]+1){
            double k=dfs(y,min(res,e[i].w));
            if(fabs(k)<eps) d[y]=0;
            e[i].w-=k;
            e[i^1].w+=k;
            res-=k;    
        }
    }
    return flow-res;
}
int q[2*N],l,r;
double ans;
bool bfs(){
    memset(d,0,sizeof d);
    d[s]=1;
    l=1,r=0;
    q[++r]=s;
    while(l<=r){
        int x=q[l++];
        //cout<<" xxx "<<x<<endl;
        for(reg i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            //cout<<" yy "<<y<<" "<<e[i].w<<endl;
            if(fabs(e[i].w)>=eps&&!d[y]){
                d[y]=d[x]+1;
                q[++r]=y;
                if(y==t) return true;
            }
        }
    }
    return false;
}
double dinic(){
    double ret=0;
    double flow=0;
    while(bfs()){
        while((flow=dfs(s,inf))>=eps) ret+=flow;
    }
    return ret;
}
int main(){
    scanf("%d%d%d",&n,&m,&p);
    int x,y,c;
    s=1,t=n;
    double R=0.00;
    for(reg i=1;i<=m;++i){
        rd(x);rd(y);rd(c);
        R=max(R,(double)c);
        b[i].x=x,b[i].y=y;b[i].c=c;
        add(x,y,(double)c);
        add(y,x,0);
    }
    int ans=dinic();
    printf("%d\n",ans);
    double L=0.00;
    R+=2.33;
    double op=0.00;
    while(R-L>=eps){
        double mid=(L+R)/2;
        memset(hd,0,sizeof hd);cnt=1;
        for(reg i=1;i<=m;++i){
            add(b[i].x,b[i].y,min((double)b[i].c,mid));
            add(b[i].y,b[i].x,0);
        }
        double now=dinic();
        if(now<ans){
            L=mid;
        }else{
            R=mid;op=mid;
        }
    }
    printf("%lf",op*p);
    return 0;
}

}
int main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2018/11/27 17:09:49
*/
費用流