HDU 5352——MZL's City——————【二分图多重匹配、拆点||网络流||费用流】

MZL's City

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 710    Accepted Submission(s): 245


Problem Description
MZL is an active girl who has her own country.

Her big country has N cities numbered from 1 to N.She has controled the country for so long and she only remebered that there was a big earthquake M years ago,which made all the roads between the cities destroyed and all the city became broken.She also remebered that exactly one of the following things happened every recent M years:

1.She rebuild some cities that are connected with X directly and indirectly.Notice that if a city was rebuilt that it will never be broken again.

2.There is a bidirectional road between city X and city Y built.

3.There is a earthquake happened and some roads were destroyed.

She forgot the exactly cities that were rebuilt,but she only knew that no more than K cities were rebuilt in one year.Now she only want to know the maximal number of cities that could be rebuilt.At the same time she want you to tell her the smallest lexicographically plan under the best answer.Notice that 8 2 1 is smaller than 10 0 1.
 

 

Input
The first contains one integer T(T<=50),indicating the number of tests.

For each test,the first line contains three integers N,M,K(N<=200,M<=500,K<=200),indicating the number of MZL’s country ,the years happened a big earthquake and the limit of the rebuild.Next M lines,each line contains a operation,and the format is “1 x” , “2 x y”,or a operation of type 3.

If it’s type 3,first it is a interger p,indicating the number of the destoyed roads,next 2*p numbers,describing the p destoyed roads as (x,y).It’s guaranteed in any time there is no more than 1 road between every two cities and the road destoyed must exist in that time.
 

 

Output
The First line Ans is the maximal number of the city rebuilt,the second line is a array of length of tot describing the plan you give(tot is the number of the operation of type 1).
 

 

Sample Input
1
5 6 2
2 1 2
2 1 3
1 1
1 2
3 1 1 2
1 2
 
 

 

Sample Output
3
0 2 1
Hint
No city was rebuilt in the third year,city 1 and city 3 were rebuilt in the fourth year,and city 2 was rebuilt in the sixth year.
 
题目大意:有n个城市,地震后需要重建,m表示有m个操作,k表示重建时最多修k座城市。有三种操作:1:修建跟x连通的城市  2:修一条a、b城市之间的路。 3:c表示地震震坏了c条路,ai、bi之间的路。问怎么重建可以重建最多的城市,且让重建时城市的数量形成的字典序最小
 
 匈牙利多重匹配:其实就是保证在匹配最大时,字典序最小。对于这种一个点可以多次匹配的问题,我们可以把点拆成k个。然后让这k个拆点跟没拆时该点应连的点分别连边。那么就形成了普通的匹配问题。要求的字典序最小,我们可以逆序跑匈牙利,然后顺序输出即可保证字典序最小,因为能匹配的在开始就尽可能多得匹配了,不能匹配的留到最后匹配。
 
#include<bits/stdc++.h>
using namespace std;
const int maxn=550;
int Map[maxn][maxn];
int vis[maxn],parent[maxn];
int match[maxn],ans[maxn];
vector<int>G[maxn*maxn];
int num,N,divp,K;
void dfs(int u){    //找出跟要重建城市x的所有连通的城市
    vis[u]=1;
    parent[num++]=u;    //记录所有连通城市编号
    for(int i=1;i<=N;i++){
        if(!vis[i]&&Map[u][i]){
            dfs(i);
        }
    }
}
bool Find(int u){   //找增广路
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        if(!vis[v]){
            vis[v]=1;
            if(!match[v]||Find(match[v])){
                match[v]=u;
                return true;
            }
        }
    }
    return false;
}
int Hungary(){  //匈牙利
    int ret=0;
    memset(ans,0,sizeof(ans));
    memset(match,0,sizeof(match));
    for(int i=divp-1;i>=0;i--){ //逆序跑匈牙利
        for(int j=i*K;j<(i+1)*K;j++){
            memset(vis,0,sizeof(vis));
            if(Find(j)){
                ret++;
                ans[i]++;
            }
        }
    }
    return ret;
}
int main(){
    int t,a,b,c,M,type,res;
    scanf("%d",&t);
    while(t--){
        memset(Map,0,sizeof(Map));
        num=0,divp=0;
        scanf("%d%d%d",&N,&M,&K);
        for(int i=0;i<M;i++){
            scanf("%d",&type);
            if(type==1){
                num=0;
                memset(vis,0,sizeof(vis));
                scanf("%d",&a);
                dfs(a);
                for(int i=0;i<num;i++){ //跟要重建的城市连通的总城市数量
                    for(int j=K*divp;j<K*(divp+1);j++){//拆点
                        G[j].push_back(parent[i]);  //从拆点向要重建的城市连边
                    }
                }
                divp++;
            }else if(type==2){
                scanf("%d%d",&a,&b);
                Map[a][b]=Map[b][a]=1;
            }else{
                scanf("%d",&c);
                for(int j=0;j<c;j++){
                    scanf("%d%d",&a,&b);
                    Map[a][b]=Map[b][a]=0;
                }
            }
        }
        res=Hungary();
        printf("%d\n",res);
        for(int i=0;i<divp;i++){    //顺序输出
            printf("%d%c",ans[i],i==divp-1?'\n':' ');
        }
        for(int i=0;i<=K*divp+K;i++){
            G[i].clear();
        }
    }
    return 0;
}

  

 

 网络流:

对于网络流建图,我们建立一个超级源点,超级汇点。然后从源点向所有的操作一连边,容量为k。从所有城市向汇点连边,容量为1。我们逆序从相应的操作一向所要重建的城市连通块中每个城市连边,容量为1,然后跑最大流,这样能保证字典序最小。

#include<bits/stdc++.h>
using namespace std;
const int maxn=750;
const int INF=0x3f3f3f3f;
struct Edge{
    int from,to,cap,flow;
};
int Map[maxn][maxn];
int vis[maxn];
vector<int>GG[maxn];
int nn;

struct Dinic{
    int n,m,s,t;
    vector<Edge>edges;
    vector<int>G[maxn];
    bool vis[maxn];
    int d[maxn];
    int cur[maxn];
    void AddEdge(int from,int to,int cap){
        edges.push_back((Edge){from,to,cap,0});
        edges.push_back((Edge){to,from,0,0});
        m=edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }
    bool BFS(){     //构造分层网络
        int x,i;
        memset(vis,0,sizeof(vis));
        queue<int>Q;
        Q.push(s);
        d[s]=0;
        vis[s]=1;
        while(!Q.empty()){
            x=Q.front(),Q.pop();
            for(i=0;i<G[x].size();i++){
                Edge & e =edges[G[x][i]];
                if(!vis[e.to]&&e.cap>e.flow){
                    vis[e.to]=1;
                    d[e.to]=d[x]+1;
                    Q.push(e.to);
                }
            }
        }
        return vis[t];
    }
    int DFS(int x,int a){       //沿阻塞流增广
        if(x==t||a==0)
            return a;
        int flow=0,f;
        for(int &i=cur[x];i<G[x].size();i++){
            Edge & e=edges[G[x][i]];
            if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0){
                e.flow+=f;
                edges[G[x][i]^1].flow-=f;
                flow+=f;
                a-=f;
                if(a==0)
                    break;
            }
        }
        return flow;
    }
    int Maxflow(int s,int t){
        this->s=s,this->t=t;
        int flow=0;
        while(BFS()){
            memset(cur,0,sizeof(cur));
            flow+= DFS(s,INF);
        }
        return flow;
    }
};
void dfs(int st,int u){ //找出跟城市u形成的连通块中的所有城市
    GG[st].push_back(u);
    vis[u]=1;
    for(int i=1;i<=nn;i++){
        if(!vis[i]&&Map[u][i]){
            dfs(st,i);
        }
    }
}
int main(){
    int m,k,ss,tt,rebnum;
    int t;
    int ST,EN;
    int res[320];
    scanf("%d",&t);
    while(t--){
        memset(Map,0,sizeof(Map));
        rebnum=0;
        scanf("%d%d%d",&nn,&m,&k);
        Dinic tmp;
        int x,y,typ;
        for(int i=0;i<m;i++){
            scanf("%d",&typ);
            if(typ==2){
                scanf("%d%d",&x,&y);
                Map[x][y]=Map[y][x]=1;
            }else if(typ==3){
                int ck=0;
                scanf("%d",&ck);
                for(int k=0;k<ck;k++){
                    scanf("%d%d",&x,&y);
                    Map[x][y]=Map[y][x]=0;
                }
            }else{
                rebnum++;   //操作一的次数
                GG[rebnum].clear();
                memset(vis,0,sizeof(vis));
                scanf("%d",&x);
                dfs(rebnum,x); //找出跟该次操作一直接或间接相连的城市
            }
        }
        ST=0,EN=rebnum+nn+1;
        for(int i=1;i<=rebnum;i++){ //源点跟操作一建容量为k的边
            tmp.AddEdge(ST,i,k);
        }
        for(int i=1;i<=nn;i++){     //城市跟汇点建容量为1的边
            tmp.AddEdge(i+rebnum,EN,1);
        }
        int ans=0;
        for(int i=rebnum;i>=1;i--){  //逆序枚举操作一
            for(int j=0;j<GG[i].size();j++){
                int v=GG[i][j];
                tmp.AddEdge( i, v+rebnum, 1);   //将每次的操作一跟直接或间接可重建的城市建边容量为1
            }
            res[i]=tmp.Maxflow(ST,EN);  //记录每次的流量
            ans+=res[i];    //总流量
        }
        printf("%d\n",ans);
        for(int i=1;i<=rebnum;i++){ //顺序输出结果
            printf("%d%c",res[i], i==rebnum? '\n':' ');
        }
    }
    return 0;
}

  

最小费用流:从源点依次向操作一建边,容量为k,费用从rebnum -> 0。从所有城市向汇点连边,容量为1,费用为0。从每个操作一向相应的城市连通块中所有城市连边,容量为1,费用为0。

 

#include<bits/stdc++.h>
using namespace std;
const int maxn = 750;
const int INF =0x3f3f3f3f;
struct Edge{
    int from,to,cap,flow,cost;
};
struct MCMF{        //最小费用流:保证在最大流量的前提下,总费用最小
    int n,m,s,t;
    vector<Edge>edges;
    vector<int>G[maxn];
    int inq[maxn];
    int d[maxn];
    int p[maxn];
    int a[maxn];
    void init(int n){
        this-> n= n;
        for(int i=0;i<n;i++)
            G[i].clear();
        edges.clear();
    }
    void AddEdge(int from,int to,int cap,int cost){
        edges.push_back((Edge){from,to,cap,0,cost});
        edges.push_back((Edge){to,from,0,0,-cost});
        m=edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }
    bool BellmanFord(int s,int t,int &flow,int &cost){  //用Bellman代替BFS找s-t的最短路
        for(int i=0;i<n;i++) d[i]=INF;
        memset(inq,0,sizeof(inq));
        d[s]=0;inq[s]=1;p[s]=0;a[s]=INF;
        queue<int>Q;
        Q.push(s);
        while(!Q.empty()){
            int u=Q.front();Q.pop();
            inq[u]=0;
            for(int i=0;i<G[u].size();i++){
                Edge & e= edges[G[u][i]];
                if(e.cap > e.flow &&d[e.to] > d[u]+e.cost){
                    d[e.to]= d[u] + e.cost;
                    p[e.to]=G[u][i];
                    a[e.to]= min(a[u],e.cap-e.flow);
                    if(!inq[e.to]){
                        Q.push(e.to);
                        inq[e.to]=1;
                    }
                }
            }
        }
        if(d[t]== INF) return false;
        flow+=a[t];
        cost += d[t]* a[t];
        int u = t ;
        while(u != s){
            edges[p[u]].flow += a[t];
            edges[p[u]^1].flow -= a[t];
            u = edges[p[u]].from;
        }
        return true;
    }
    int Mincost(int s,int t){
        int flow = 0, cost = 0;
        while(BellmanFord ( s, t, flow, cost));
     //   return cost;              // 可以返回最小花费
        return flow;                //返回最大流量
    }
};
int n;
int Map[maxn][maxn],vis[maxn];
vector<int>Vc[maxn];
void dfs(int rb,int u){
    Vc[rb].push_back(u);
    vis[u]=1;
    for(int i=1;i<=n;i++){
        if(!vis[i]&&Map[u][i]){
            dfs(rb,i);
        }
    }
}
int main(){
    int t,ST,EN,m,k;
    scanf("%d",&t);
    while(t--){
        memset(Map,0,sizeof(Map));
        MCMF tmp;
        scanf("%d%d%d",&n,&m,&k);
        int x,y,rebnum=0,typ;
        for(int i=0;i<m;i++){
            scanf("%d",&typ);
            if(typ==2){
                scanf("%d%d",&x,&y);
                Map[x][y]=Map[y][x]=1;
            }else if(typ==3){
                int ck;
                scanf("%d",&ck);
                for(int j=0;j<ck;j++){
                    scanf("%d%d",&x,&y);
                    Map[x][y]=Map[y][x]=0;
                }
            }else {
                rebnum++;
                scanf("%d",&x);
                Vc[rebnum].clear();
                memset(vis,0,sizeof(vis));
                dfs(rebnum,x);
            }
        }
        ST=0,EN=rebnum+n+1;
        tmp.init(EN+1);
        for(int i=1;i<=rebnum;i++){     //从源点向操作一建边,容量为k,费用依次减小以保证字典序最小
            tmp.AddEdge(ST,i,k,rebnum-i);
        }
        for(int i=1;i<=n;i++){      //从所有城市向汇点建边,容量为1,费用为0
            tmp.AddEdge(i+rebnum,EN,1,0);
        }
        int ans=0,res[250];
        for(int i=rebnum;i>=1;i--){     //逆向建边。
            for(int j=0;j<Vc[i].size();j++){
                int v=Vc[i][j];
                tmp.AddEdge(i,rebnum+v,1,0);    //从操作一向所有跟该次操作一直接或间接连接的城市建边,容量为1,费用为0
            }
            res[i]=tmp.Mincost(ST,EN);      //类中这次返回的是最大流量,而不是最小费用
            ans+=res[i];
        }
        printf("%d\n",ans);
        for(int i=1;i<=rebnum;i++){
            printf("%d%c",res[i],i==rebnum? '\n':' ');
        }
    }
    return 0;
}

  

posted @ 2015-08-07 10:34  tcgoshawk  阅读(234)  评论(0编辑  收藏  举报