poj2175 Evacuation Plan// 最小费用流 (dijstra || spfa找负环增广)

poj2175 Evacuation Plan// 最小费用流 (dijstra || spfa找负环增广)

 poj2175 Evacuation Plan// 最小费用流 (dijstra || spfa找负环增广)

 poj2175 Evacuation Plan// 最小费用流 (dijstra || spfa找负环增广)

 求n个建筑的人,到m个防空洞的最小路径和。

直接建立最小流,n*m条边,每个建筑到防空洞。

直接跑最小流,我奇迹的卡过了(969ms/1000ms) dijstar 真的好用。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<climits>
#include<cmath>
#include<vector>
#include<cstring>
#include<queue>
using namespace std;
#define long long LL
const int max_n=1005;
struct bno{int x,y,z;}bu[105],sh[105];
struct no{int to,cap,cost,rev;};
struct qno{int v,d;}; //队列节点
bool operator<(const qno &x,const qno &y) {return x.d>y.d;} //队列cmp重写
int n,m;  //定点数 和边数
vector<no>g[max_n];
int dis[max_n],h[max_n]; //最短距离,顶点的势
int prevv[max_n],preve[max_n]; //在vector下记录路径
int mp[105][105];
void addedge(int from,int to,int cap,int cost){
    g[from].push_back((no){to,cap,cost,g[to].size()});
    g[to].push_back((no){from,0,-cost,g[from].size()-1});
}
int min_cost_flow(int s,int t,int f){ //注意n变化了!!!!!!!!!!!!!!!!!
    int res=0; //answer
    for(int i=0;i<=n;i++) h[i]=0; //初始化h
    while(f>0){
        priority_queue<qno>q;
        for(int i=0;i<=n;i++) dis[i]=INT_MAX/2;
        dis[s]=0;
        q.push((qno){s,0});
        while(!q.empty()){
            qno now=q.top();q.pop();
            int v=now.v;
            if(dis[v]<now.d) continue;
            for(int i=0;i<g[v].size();i++){
                no &e=g[v][i];
                if(e.cap>0&&dis[e.to]>dis[v]+e.cost+h[v]-h[e.to]){ //加入啦势
                    dis[e.to]=dis[v]+e.cost+h[v]-h[e.to];
                    prevv[e.to]=v;
                    preve[e.to]=i;
                    q.push((qno){e.to,dis[e.to]});
                }
            }
        }
        if(dis[t]==INT_MAX/2) return -1;
        for(int v=0;v<=n;v++) h[v]+=dis[v];
        int d=f; //找增广路
        for(int v=t;v!=s;v=prevv[v]) d=min(d,g[prevv[v]][preve[v]].cap);
        f-=d;res+=d*h[t];
        for(int v=t;v!=s;v=prevv[v]){
            no &e=g[prevv[v]][preve[v]];
            e.cap-=d;
            g[v][e.rev].cap+=d;
        }
    }
    return res;
}

int main(){
    ios::sync_with_stdio(false);cin.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>bu[i].x>>bu[i].y>>bu[i].z;
    for(int i=1;i<=m;i++)
        cin>>sh[i].x>>sh[i].y>>sh[i].z;
    int cp=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            mp[i][j]=abs(bu[i].x-sh[j].x)+abs(bu[i].y-sh[j].y)+1;
            addedge(i,j+n,bu[i].z,mp[i][j]);
        }
        cp+=bu[i].z;
    }
    for(int i=1;i<=n;i++) addedge(0,i,bu[i].z,0);
    for(int i=n+1;i<=n+m;i++)addedge(i,n+m+1,sh[i-n].z,0);
    n=n+m+1;
    int ans=min_cost_flow(0,n,cp);
    int x=0;
    n=n-m-1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            int a;cin>>a;
            x=x+mp[i][j]*a;
        }
    }
    if(x==ans) {cout<<"OPTIMAL";return 0;}
    cout<<"SUBOPTIMAL"<<endl;
    for(int i=1;i<=n;i++){
        for(int j=0;j<m;j++)
            cout<<bu[i].z-g[i][j].cap<<' ';
        cout<<endl;
    }
    return 0;
}

更好的解法是 , 在已有的case上找负环,运行要快一半,(可以用Floyd或者spfa)(还没搞懂,先贴代码)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int Max_v=1100;

int N,M;
struct Node{
    int x,y,c;
}no[Max_v];
int E[Max_v][Max_v];
int d[Max_v][Max_v]; //距离矩阵

int dist[Max_v],cnt[Max_v],prv[Max_v];
bool used[Max_v];

void spfa(int t){
    memset(dist,0x3f,sizeof(dist));
    memset(cnt,0,sizeof(cnt));
    memset(used,0,sizeof(used));
    queue<int>que;
    que.push(t);
    dist[t]=0;used[t]=1;cnt[t]=1;
    while(!que.empty()){
        int u=que.front();que.pop();
        used[u]=0;
        for(int i=0;i<=t;i++){
            if(d[u][i]!=inf&&dist[i]>dist[u]+d[u][i]){
                dist[i]=dist[u]+d[u][i];
                prv[i]=u;
                if(!used[i]){
                    used[i]=1;cnt[i]++;
                    que.push(i);
                    if(cnt[i]>t){
                        printf("SUBOPTIMAL\n");
                        memset(used,0,sizeof(used));
                        used[i]=1;
                        int sta; //找到一个负圈上的点
                        for(int j=prv[i];!used[j];j=prv[j]){
                            used[j]=1;
                            sta=j;
                        }
                        int u=prv[sta],v=sta;
                        do{ 
                            if(u>N)E[v][u]--;
                            else E[u][v]++;
                            v=u;u=prv[u];
                        }while(v!=sta);
                        for(int i=1;i<=N;i++){
                            for(int j=N+1;j<=N+M;j++)
                                printf("%d%c",E[i][j],j==N+M?'\n':' ');
                        }
                        return;
                    }
                }
            }
        }
    }
    printf("OPTIMAL\n");
}

int main()
{
    scanf("%d%d",&N,&M);
    for(int i=1;i<=N+M;i++){
        scanf("%d%d%d",&no[i].x,&no[i].y,&no[i].c);
    }
    int s=0,t=N+M+1;
    memset(d,0x3f,sizeof(d));
    int x[Max_v],y[Max_v];
    memset(x,0,sizeof(x));
    memset(y,0,sizeof(y));
    for(int i=1;i<=N;i++){ //构造残余网络
        for(int j=N+1;j<=N+M;j++){
            scanf("%d",&E[i][j]);
            x[i]+=E[i][j];
            y[j]+=E[i][j];
            int cost=abs(no[i].x-no[j].x)+abs(no[i].y-no[j].y)+1;
            d[i][j]=cost;
            if(E[i][j]>0)d[j][i]=-cost;
        }
    }
    for(int i=1;i<=N;i++){
        if(x[i]<no[i].c)d[s][i]=0;
        if(x[i])d[i][s]=0;
    }
    for(int i=N+1;i<=N+M;i++){
        if(y[i]<no[i].c)d[i][t]=0;
        if(y[i])d[t][i]=0;
    }
    spfa(t);
    return 0;
}

Floyd找负环解法:https://blog.****.net/u011008379/article/details/38983451