1. 程式人生 > 實用技巧 >《網路流24題》(未完待續)

《網路流24題》(未完待續)

主要是想把這些題幹掉

很多都還沒寫,慢慢來

某些題目可以不需要網路流的emm就再說吧


餐巾計劃問題

Luogu P1251 難度:省選/NOI-



Submit: NaN




家園 / 星際轉移問題

Luogu P2754 難度:省選/NOI-



Submit: NaN




飛行員配對方案問題(最大流)

Luogu P2756 難度:提高+/省選-


模板二分圖最大匹配,可直接跑最大流


Submit: 2020-07-30 11:42

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;

const int maxn=666,maxm=233333;
struct edge{
    int u,v,cap,flow;
    edge(){}
    edge(int u,int v,int cap,int flow):u(u),v(v),cap(cap),flow(flow){}
}eg[maxm<<1];
int tot,s,t,dis[maxn<<1],cur[maxn<<1];
vector<int> tab[maxn<<1];

void init(int n,int s_,int t_){
    while(n)tab[n--].clear();
    tot=0,s=s_,t=t_;
}
void addedge(int u,int v,int cap){
    tab[u].push_back(tot);
    eg[tot++]=edge(u,v,cap,0);
    tab[v].push_back(tot);
    eg[tot++]=edge(v,u,0,0);
}
int bfs(){
    queue<int> q;
    q.push(s);
    memset(dis,INF,sizeof dis);
    dis[s]=0;
    while(!q.empty()){
        int h=q.front(),i;
        q.pop();
        for(i=0;i<tab[h].size();i++){
            edge &e=eg[tab[h][i]];
            if(e.cap>e.flow&&dis[e.v]==INF){
                dis[e.v]=dis[h]+1;
                q.push(e.v);
            }
        }
    }
    return dis[t]<INF;
}
int dfs(int x,int maxflow){
    if(x==t|maxflow==0)
        return maxflow;
    int flow=0,i,f;
    for(i=cur[x];i<tab[x].size();i++){
        cur[x]=i;
        edge &e=eg[tab[x][i]];
        if(dis[e.v]==dis[x]+1&&(f=dfs(e.v,min(maxflow,e.cap-e.flow)))>0){
            e.flow+=f;
            eg[tab[x][i]^1].flow-=f;
            flow+=f;
            maxflow-=f;
            if(maxflow==0)
                break;
        }
    }
    return flow;
}
int dinic(){
    int flow=0;
    while(bfs()){
        memset(cur,0,sizeof(cur));
        flow+=dfs(s,INF);
    }
    return flow;
}

void solve()
{
    int n,m,a,b;
    cin>>m>>n;
    init(t,n+1,n+2);
    while(cin>>a>>b)
    {
        if(a==-1&&b==-1)
            break;
        addedge(a,b,INF);
    }
    for(int i=1;i<=m;i++)
        addedge(s,i,1);
    for(int i=m+1;i<=n;i++)
        addedge(i,t,1);
    cout<<dinic()<<'\n';
    for(int i=0;i<tot;i+=2)
        if(eg[i].u!=s&&eg[i].v!=t)
        {
            if(eg[i^1].flow!=0) //反向邊流量不為0說明選擇了該邊
                cout<<eg[i].u<<' '<<eg[i].v<<'\n';
        }
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}



軟體補丁問題(最短路、狀態壓縮)

Luogu P2761 難度:提高+/省選-


bug只有\(20\)種,所以可以狀態壓縮為二進位制成\(2^{20}\)種狀態表示

其後以起點為均為\(1\)的狀態(\(2^n-1\)),終點為\(0\)跑最短路即可求出答案

精髓在於位運算


Submit: 2020-07-30 18:23

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxd=1<<21;

struct patch
{
    int b1,b2,f1,f2,t;
}ar[110];

int n,m,dis[maxd+10];
bool inq[maxd+10];

void spfa(int st) //從初始狀態跑最短路
{
    memset(dis,INF,sizeof dis);
    dis[st]=0;
    queue<int> q;
    q.push(st);
    while(!q.empty())
    {
        int bef=q.front();
        q.pop();
        for(int i=1;i<=m;i++) //列舉去嘗試使用各種補丁
        {
            if((bef&ar[i].b1)==ar[i].b1&&(bef&ar[i].b2)==0) //滿足所述的補丁使用條件時
            {
                int aft=(bef|ar[i].f1|ar[i].f2)^ar[i].f1; //獲取使用後狀態
                if(dis[aft]>dis[bef]+ar[i].t)
                {
                    dis[aft]=dis[bef]+ar[i].t;
                    if(!inq[aft])
                    {
                        q.push(aft);
                        inq[aft]=true;
                    }
                }
            }
        }
        inq[bef]=false;
    }
}

void solve()
{
    string str;
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        cin>>ar[i].t;
        cin>>str;
        for(int j=0;j<n;j++)
        {
            if(str[j]=='+')
                ar[i].b1|=(1<<j);
            else if(str[j]=='-')
                ar[i].b2|=(1<<j);
        }
        cin>>str;
        for(int j=0;j<n;j++)
        {
            if(str[j]=='-')
                ar[i].f1|=(1<<j);
            else if(str[j]=='+')
                ar[i].f2|=(1<<j);
        }
    }
    spfa((1<<n)-1); //初始狀態(n個1)
    if(dis[0]==INF)
        dis[0]=0;
    cout<<dis[0]<<'\n';
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}



太空飛行計劃問題

Luogu P2762 難度:省選/NOI-



Submit: NaN




試題庫問題(最大流)

Luogu P2763 難度:省選/NOI-


明顯題目與型別之間應該存在一條流量為\(1\)的邊表示選擇或不選擇

型別\(i\)與源點/匯點間應該存在一條流量為\(cnt[i]\)表示選擇的題目數量

故建圖方法之一如下:

  • 源點\(s\)向型別\(i\)連一條流量為\(cnt[i]\)的邊

  • 型別\(i\)向可歸類的題目\(j\)連一條流量為\(1\)的邊

  • 題目\(j\)向匯點\(t\)連一條流量為\(1\)的邊

跑最大流,如果最大流\(flow_{max}\)

小於需要選擇的題目數量總和\(sum_{cnt}\),則說明不存在任何一種歸類方案

否則遍歷每條正向邊,如果邊的型別是“從型別連向題目”的邊,且此時該邊的流量不為\(0\),則說明型別\(i\)選擇了題目\(j\),儲存輸出即可


Submit: 2020-07-31 12:48

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=2222,maxm=100000;

struct edge{
    int u,v,cap,flow;
    edge(){}
    edge(int u,int v,int cap,int flow):u(u),v(v),cap(cap),flow(flow){}
}eg[maxm<<1];
int tot,s,t,dis[maxn<<1],cur[maxn<<1];
vector<int> tab[maxn<<1];

void init(int n,int s_,int t_){
    while(n)tab[n--].clear();
    tot=0,s=s_,t=t_;
}
void addedge(int u,int v,int cap){
    tab[u].push_back(tot);
    eg[tot++]=edge(u,v,cap,0);
    tab[v].push_back(tot);
    eg[tot++]=edge(v,u,0,0);
}
int bfs(){
    queue<int> q;
    q.push(s);
    memset(dis,INF,sizeof dis);
    dis[s]=0;
    while(!q.empty()){
        int h=q.front(),i;
        q.pop();
        for(i=0;i<tab[h].size();i++){
            edge &e=eg[tab[h][i]];
            if(e.cap>e.flow&&dis[e.v]==INF){
                dis[e.v]=dis[h]+1;
                q.push(e.v);
            }
        }
    }
    return dis[t]<INF;
}
int dfs(int x,int maxflow){
    if(x==t|maxflow==0)
        return maxflow;
    int flow=0,i,f;
    for(i=cur[x];i<tab[x].size();i++){
        cur[x]=i;
        edge &e=eg[tab[x][i]];
        if(dis[e.v]==dis[x]+1&&(f=dfs(e.v,min(maxflow,e.cap-e.flow)))>0){
            e.flow+=f;
            eg[tab[x][i]^1].flow-=f;
            flow+=f;
            maxflow-=f;
            if(maxflow==0)
                break;
        }
    }
    return flow;
}
int dinic(){
    int flow=0;
    while(bfs()){
        memset(cur,0,sizeof(cur));
        flow+=dfs(s,INF);
    }
    return flow;
}

int cnt[25];
void solve()
{
    int k,n,p,d,sum=0;
    cin>>k>>n;
    init(k+n+2,k+n+1,k+n+2);
    for(int i=1;i<=k;i++)
    {
        cin>>cnt[i];
        sum+=cnt[i];
        addedge(s,i+n,cnt[i]);
    }
    for(int i=1;i<=n;i++)
    {
        addedge(i,t,1);
        cin>>p;
        while(p--)
        {
            cin>>d;
            addedge(d+n,i,1);
        }
    }
    int mxflow=dinic();
    if(mxflow!=sum)
    {
        cout<<"No Solution!\n";
        return;
    }
    vector<int> ans[25];
    for(int i=0;i<tot;i+=2)
    {
        if(eg[i].flow&&eg[i].v<=n)
            ans[eg[i].u-n].push_back(eg[i].v);
    }
    for(int i=1;i<=k;i++)
    {
        cout<<i<<':';
        for(int it:ans[i])
            cout<<' '<<it;
        cout<<'\n';
    }
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}



最小路徑覆蓋問題

Luogu P2764 難度:省選/NOI-



Submit: NaN




魔術球問題(貪心)

Luogu P2765 難度:省選/NOI-


這題可以直接暴力+貪心

每次for一遍看看能不能放就完事兒了

這題要求相鄰和為平方數,而平方數與遞增數之間的特點導致可以這樣直接貪心

但如果像某道漢諾塔題,要求相鄰和為質數,那就不能這樣貪心了

程式碼實現的話,簡單STL應用題吧


Submit: 2020-07-31 14:53

#include<bits/stdc++.h>
using namespace std;

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    
    int n,m=0,id=1;
    set<int> st;
    stack<int> sk[60];
    
    cin>>n;
    
    for(int i=1;i<=100;i++)
        st.insert(i*i);
    
    for(;;id++)
    {
        bool flag=false;
        for(int i=1;i<=m;i++)
        {
            if(st.find(sk[i].top()+id)!=st.end())
            {
                sk[i].push(id);
                flag=true;
                break;
            }
        }
        if(flag)
            continue;
        if(m==n)
            break;
        sk[++m].push(id);
    }
    
    cout<<id-1<<'\n';
    
    stack<int> skk; //棧倒序輸出
    for(int i=1;i<=n;i++)
    {
        while(!sk[i].empty())
        {
            skk.push(sk[i].top());
            sk[i].pop();
        }
        while(!skk.empty())
        {
            cout<<skk.top()<<' ';
            skk.pop();
        }
        cout<<'\n';
    }
    
    return 0;
}



最長不下降子序列問題

Luogu P2766 難度:省選/NOI-


本題共有三問

第一問為基礎動態規劃

第二問需要建立網路流

首先將每個數字拆成兩個點,分置左右兩側

從所有不下降子序列的第一個數字入手,連一條從源點來的邊,流量為\(1\)

從所有不下降子序列的最後一個數字入手,連一條去匯點的邊,流量為\(1\)

所有不下降子序列相鄰點之間也用流量為\(1\)的邊連線

其後直接跑最大流,就能得出第二問答案

第三問只是在第二問的基礎上加了兩個條件,\(x_1\)可以無限用,所以連一條從源點來的流量為\(\infty\)的邊,再連一條到自己拆出去的點的流量為\(\infty\)的邊

\(x_n\)不一定是最長不下降子序列的終點,所以對其做出限制,只有在它是最長不下降子序列的終點時,才建立一條從自己連向拆點後的點的流量為\(\infty\)的邊,再建一條拆後的點流向匯點的流量為\(\infty\)的邊

在第二問的基礎上再跑一次最大流,答案加上第二問的答案即是第三問的答案


Submit: 2020-07-31 18:02

#include<bits/stdc++.h>
using namespace std;
const int maxn=666,maxm=23333;
const int INF=0x3f3f3f3f;

struct edge{
    int u,v,cap,flow;
    edge(){}
    edge(int u,int v,int cap,int flow):u(u),v(v),cap(cap),flow(flow){}
}eg[maxm<<1];
int tot,s,t,dis[maxn<<1],cur[maxn<<1];
vector<int> tab[maxn<<1];

void init(int n,int s_,int t_){
    while(n)tab[n--].clear();
    tot=0,s=s_,t=t_;
}
void addedge(int u,int v,int cap){
    tab[u].push_back(tot);
    eg[tot++]=edge(u,v,cap,0);
    tab[v].push_back(tot);
    eg[tot++]=edge(v,u,0,0);
}
int bfs(){
    queue<int> q;
    q.push(s);
    memset(dis,INF,sizeof dis);
    dis[s]=0;
    while(!q.empty()){
        int h=q.front(),i;
        q.pop();
        for(i=0;i<tab[h].size();i++){
            edge &e=eg[tab[h][i]];
            if(e.cap>e.flow&&dis[e.v]==INF){
                dis[e.v]=dis[h]+1;
                q.push(e.v);
            }
        }
    }
    return dis[t]<INF;
}
int dfs(int x,int maxflow){
    if(x==t|maxflow==0)
        return maxflow;
    int flow=0,i,f;
    for(i=cur[x];i<tab[x].size();i++){
        cur[x]=i;
        edge &e=eg[tab[x][i]];
        if(dis[e.v]==dis[x]+1&&(f=dfs(e.v,min(maxflow,e.cap-e.flow)))>0){
            e.flow+=f;
            eg[tab[x][i]^1].flow-=f;
            flow+=f;
            maxflow-=f;
            if(maxflow==0)
                break;
        }
    }
    return flow;
}
int dinic(){
    int flow=0;
    while(bfs()){
        memset(cur,0,sizeof(cur));
        flow+=dfs(s,INF);
    }
    return flow;
}

int n,ar[555],dp[555];
int ans1=0,ans2=0,ans3=0;

void dealQue1()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<i;j++)
            if(ar[j]<=ar[i])
                dp[i]=max(dp[i],dp[j]);
        dp[i]++;
        ans1=max(ans1,dp[i]);
    }
    cout<<ans1<<'\n';
}

void dealQue2()
{
    init(2*n+2,2*n+1,2*n+2);
    for(int i=1;i<=n;i++)
    {
        addedge(i,i+n,1);
        if(dp[i]==1) //為前i個數中最小的數,即不存在前節點
            addedge(s,i,1);
        if(dp[i]==ans1) //最長上升子序列的最後一個數值,即不再有後節點
            addedge(i,t,1);
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<i;j++)
            if(ar[j]<=ar[i]&&dp[j]+1==dp[i]) //同一上升子序列的相鄰節點
                addedge(j+n,i,1);
    ans2=dinic();
    cout<<ans2<<'\n';
}

void dealQue3()
{
    ans3=ans2;
    addedge(s,1,INF);
    addedge(1,n+1,INF);
    if(dp[n]==ans1&&n!=1)
    {
        addedge(n,n+n,INF);
        addedge(n+n,t,INF);
    }
    ans3+=dinic();
    cout<<ans3<<'\n';
}

void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>ar[i];
    dealQue1();
    dealQue2();
    dealQue3();
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}



航空路線問題

Luogu P2770 難度:省選/NOI-



Submit: NaN




方格取數問題(最小割)

Luogu P2774 難度:省選/NOI-


反向考慮該題,我們使用最小割來計算出最少應該去掉多少數字使得留下的數字滿足題意且和最大

首先可以想到,行列和奇偶性相同的位置一定不是相鄰的,所以可以根據行列和的奇偶性將整張圖的數字分成兩個集合

源點連行列和為奇數的點,流量為該點對應的數字

行列和為偶數的點連匯點,流量為該點對應的數字

又因為相鄰的點奇偶性一定不同(差值為\(1\)

所以當我們找到一個行列和為奇數的點時,可以讓其向相鄰的點連一條流量為\(\infty\)的邊

最後跑一遍最小割,得到的結果即為最少應該去掉的數字之和

取原來全圖的數字之和減去便是本題答案


Submit: 2020-07-31 18:52

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=22222,maxm=666666;

struct edge{
    int u,v,cap,flow;
    edge(){}
    edge(int u,int v,int cap,int flow):u(u),v(v),cap(cap),flow(flow){}
}eg[maxm<<1];
int tot,s,t,dis[maxn<<1],cur[maxn<<1];
vector<int> tab[maxn<<1];

void init(int n,int s_,int t_){
    while(n)tab[n--].clear();
    tot=0,s=s_,t=t_;
}
void addedge(int u,int v,int cap){
    tab[u].push_back(tot);
    eg[tot++]=edge(u,v,cap,0);
    tab[v].push_back(tot);
    eg[tot++]=edge(v,u,0,0);
}
int bfs(){
    queue<int> q;
    q.push(s);
    memset(dis,INF,sizeof dis);
    dis[s]=0;
    while(!q.empty()){
        int h=q.front(),i;
        q.pop();
        for(i=0;i<tab[h].size();i++){
            edge &e=eg[tab[h][i]];
            if(e.cap>e.flow&&dis[e.v]==INF){
                dis[e.v]=dis[h]+1;
                q.push(e.v);
            }
        }
    }
    return dis[t]<INF;
}
int dfs(int x,int maxflow){
    if(x==t|maxflow==0)
        return maxflow;
    int flow=0,i,f;
    for(i=cur[x];i<tab[x].size();i++){
        cur[x]=i;
        edge &e=eg[tab[x][i]];
        if(dis[e.v]==dis[x]+1&&(f=dfs(e.v,min(maxflow,e.cap-e.flow)))>0){
            e.flow+=f;
            eg[tab[x][i]^1].flow-=f;
            flow+=f;
            maxflow-=f;
            if(maxflow==0)
                break;
        }
    }
    return flow;
}
int dinic(){
    int flow=0;
    while(bfs()){
        memset(cur,0,sizeof(cur));
        flow+=dfs(s,INF);
    }
    return flow;
}

int n,m;

inline int id(int x,int y)
{
    return (x-1)*m+y;
}

void solve()
{
    int d,sum=0;
    cin>>n>>m;
    init(0,n*m+1,n*m+2);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            cin>>d;
            sum+=d;
            if((i+j)&1)
            {
                addedge(s,id(i,j),d);
                if(i!=1)
                    addedge(id(i,j),id(i-1,j),INF);
                if(j!=1)
                    addedge(id(i,j),id(i,j-1),INF);
                if(i!=n)
                    addedge(id(i,j),id(i+1,j),INF);
                if(j!=m)
                    addedge(id(i,j),id(i,j+1),INF);
            }
            else
                addedge(id(i,j),t,d);
        }
    cout<<sum-dinic()<<'\n';
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}



機器人路徑規劃問題

Luogu P2775 難度:NOI/NOI+/CTSC



Submit: NaN




圓桌問題

Luogu P3254 難度:省選/NOI-



Submit: NaN




騎士共存問題

Luogu P3355 難度:省選/NOI-



Submit: NaN




火星探險問題

Luogu P3356 難度:省選/NOI-



Submit: NaN




最長k可重線段集問題

Luogu P3357 難度:省選/NOI-



Submit: NaN




最長k可重區間集問題

Luogu P3358 難度:省選/NOI-



Submit: NaN




汽車加油行駛問題

Luogu P4009 難度:省選/NOI-



Submit: NaN




孤島營救問題(BFS、狀態壓縮)

Luogu P4011 難度:提高+/省選-


帶多條件的普通搜尋

可以將鑰匙種類轉化成二進位制表示

開四維陣列\(mp[x_i][y_i][x_j][y_j]\)儲存\((x_i,y_i)\)\((x_j,y_j)\)之間的道路性質,不能通過為\(-1\),能直接通過為\(0\),其餘則為門的種類

開三維陣列\(vis[x][y][status]\)儲存走到\((x,y)\)且鑰匙持有狀態為\(status\)的狀態是否已經訪問過,相當於防止了死迴圈出現

開二維陣列\(key[x][y]\)儲存\((x,y)\)所擁有的鑰匙種類

於是便是普通bfs搜尋,每次先判斷下一點是否合法(\(1≤i≤n,\ 1≤j≤m\)),再檢查能否直接通過或不能通過或暫時不能通過(沒有鑰匙,即進行按位與運算結果為\(0\)),最後檢查是否已經訪問過這一狀態(\(vis\)陣列)

對於搜尋過程的狀態,開一個結構體\(\{x,y,step,k\}\)儲存當前位置為\((x,y)\),步數為\(step\),鑰匙持有狀態為\(k\)的狀態


Submit: 2020-07-30 20:48

#include<bits/stdc++.h>
using namespace std;

struct node
{
    int x,y,step,k;
};

int n,m,mp[11][11][11][11],key[11][11];
int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
bool vis[11][11][1<<15];

inline bool prim(int x,int y)
{
    return x>0&&y>0&&x<=n&&y<=m;
}

int bfs()
{
    queue<node> q;
    q.push(node{1,1,0,0}); //初始狀態
    vis[1][1][0]=true;
    while(!q.empty())
    {
        node nd=q.front();
        q.pop();
        for(int i=0;i<4;i++)
        {
            int px=nd.x+dx[i],py=nd.y+dy[i];
            if(prim(px,py))
            {
                if(mp[nd.x][nd.y][px][py]==-1)
                    continue; //無法走過
                if(mp[nd.x][nd.y][px][py]>0&&(nd.k&(1<<mp[nd.x][nd.y][px][py]))==0)
                    continue; //沒有鑰匙
                if(!vis[px][py][nd.k|key[px][py]]) //這裡直接將下一格的鑰匙拿來即可
                {
                    if(px==n&&py==m)
                        return nd.step+1; //走到目標點,輸出前一狀態的步數+1即可
                    vis[px][py][nd.k|key[px][py]]=true;
                    q.push(node{px,py,nd.step+1,nd.k|key[px][py]});
                }
            }
        }
    }
    return -1; //無解
}

void solve()
{
    int p,k,g,x1,x2,y1,y2,s,x,y,q;
    cin>>n>>m>>p>>k;
    while(k--)
    {
        cin>>x1>>y1>>x2>>y2>>g;
        if(g==0) //讓0表示互通的狀態
            g=-1;
        mp[x1][y1][x2][y2]=mp[x2][y2][x1][y1]=g;
    }
    cin>>s;
    while(s--)
    {
        cin>>x>>y>>q;
        key[x][y]|=1<<q;
    }
    cout<<bfs()<<'\n';
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}



深海機器人問題

Luogu P4012 難度:省選/NOI-



Submit: NaN




數字梯形問題

Luogu P4013 難度:省選/NOI-



Submit: NaN




分配問題(最小費用最大流)

Luogu P4014 難度:省選/NOI-


最小費用最大流模板

因為每個工人都要做一個工件,所以只要把費用設定為\(1\)直接跑就行

如果求的是最小流,設定正數邊後直接跑即可

如果求的是最大流,設定負數邊後直接跑並且答案取反


Submit: 2020-07-31 19:27

#include<bits/stdc++.h>
using namespace std;
const int maxn=222;
 
struct MCMF {
    struct E {
        int from, to, cap, v;
        E() {}
        E(int f, int t, int cap, int v) : from(f), to(t), cap(cap), v(v) {}
    };
    int n, m, s, t;
    vector<E> edges;
    vector<int> G[maxn];
    bool inq[maxn];
    int dis[maxn], pre[maxn], a[maxn];
    void init(int _n, int _s, int _t) {
        n = _n; s = _s; t = _t;
        for (int i = 0; i <= n; i++)
            G[i].clear();
        edges.clear();
        m = 0;
    }
    void add(int from, int to, int cap, int cost) {
        edges.emplace_back(from, to, cap, cost);
        edges.emplace_back(to, from, 0, -cost);
        G[from].push_back(m++);
        G[to].push_back(m++);
    }
    bool spfa() {
        for (int i = 0; i <= n; i++) {
            dis[i] = 1e9;
            pre[i] = -1;
            inq[i] = false;
        }
        dis[s] = 0, a[s] = 1e9, inq[s] = true;
        queue<int> Q; Q.push(s);
        while (!Q.empty()) {
            int u = Q.front(); Q.pop();
            inq[u] = false;
            for (int& idx: G[u]) {
                E& e = edges[idx];
                if (e.cap && dis[e.to] > dis[u] + e.v) {
                    dis[e.to] = dis[u] + e.v;
                    pre[e.to] = idx;
                    a[e.to] = min(a[u], e.cap);
                    if (!inq[e.to]) {
                        inq[e.to] = true;
                        Q.push(e.to);
                    }
                }
            }
        }
        return pre[t] != -1;
    }
    int solve() {
        int flow = 0, cost = 0;
        while (spfa()) {
            flow += a[t];
            cost += a[t] * dis[t];
            int u = t;
            while (u != s) {
                edges[pre[u]].cap -= a[t];
                edges[pre[u] ^ 1].cap += a[t];
                u = edges[pre[u]].from;
            }
        }
        return cost;
    }
}f1,f2;

void solve()
{
    int n,d;
    cin>>n;
    f1.init(n*2+2,n*2+1,n*2+2);
    f2.init(n*2+2,n*2+1,n*2+2);
    for(int i=1;i<=n;i++)
    {
        f1.add(f1.s,i,1,0);
        f1.add(n+i,f1.t,1,0);
        f2.add(f2.s,i,1,0);
        f2.add(n+i,f2.t,1,0);
        for(int j=1;j<=n;j++)
        {
            cin>>d;
            f1.add(i,n+j,1,d);
            f2.add(i,n+j,1,-d);
        }
    }
    cout<<f1.solve()<<'\n'<<-f2.solve()<<'\n';
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}



運輸問題

Luogu P4015 難度:省選/NOI-



Submit: NaN




負載平衡問題(貪心)

Luogu P4016 難度:提高+/省選-


這啥?

列舉不需要交換的相鄰兩人,\(O(n)\)列舉\(O(n)\)判斷即可


Submit: 2020-07-31 10:03

#include<bits/stdc++.h>
using namespace std;

int n,cnt[110],ave;

int deal(int st)
{
    int res=0,tmp=0,d;
    for(int i=st,j=1;j<n;i=i%n+1,j++) //迴圈,判斷n-1次
    {
        d=tmp+cnt[i]-ave;
        res+=abs(d);
        tmp=d;
    }
    return res;
}
void solve()
{
    int sum=0,ans=INT_MAX;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>cnt[i],sum+=cnt[i];
    ave=sum/n;
    for(int i=1;i<=n;i++) //列舉i與i+1無需交換
        ans=min(ans,deal(i));
    cout<<ans<<'\n';
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}