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
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.
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.
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.
#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;
}
HDU 5352——MZL's City——————【二分图多重匹配、拆点||网络流||费用流】的更多相关文章
- Hdu 5352 MZL's City (多重匹配)
题目链接: Hdu 5352 MZL's City 题目描述: 有n各节点,m个操作.刚开始的时候节点都是相互独立的,一共有三种操作: 1:把所有和x在一个连通块内的未重建过的点全部重建. 2:建立一 ...
- HDU 5352 MZL's City (2015 Multi-University Training Contest 5)
题目大意: 一个地方的点和道路在M年前全部被破坏,每年可以有三个操作, 1.把与一个点X一个联通块内的一些点重建,2.连一条边,3.地震震坏一些边,每年最多能重建K个城市,问最多能建多少城市,并输出操 ...
- 2015 Multi-University Training Contest 5 hdu 5352 MZL's City
MZL's City Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total ...
- HDU 5352 MZL's City
最小费用最大流,因为要控制字典序,网络流控制不好了...一直WA,所以用了费用流,时间早的费用大,时间晚的费用少. 构图: 建立一个超级源点和超级汇点.超级源点连向1操作,容量为K,费用为COST,然 ...
- poj 2289 Jamie's Contact Groups【二分+最大流】【二分图多重匹配问题】
题目链接:http://poj.org/problem?id=2289 Jamie's Contact Groups Time Limit: 7000MS Memory Limit: 65536K ...
- hdu 3605(二分图多重匹配)
Escape Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Subm ...
- HDU - 3605 Escape (缩点+最大流/二分图多重匹配)
题意:有N(1<=N<=1e5)个人要移民到M(1<=M<=10)个星球上,每个人有自己想去的星球,每个星球有最大承载人数.问这N个人能否移民成功. 分析:可以用最大流的思路求 ...
- HDU 1669 二分图多重匹配+二分
Jamie's Contact Groups Time Limit: 15000/7000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/ ...
- HDU 3605 Escape(二分图多重匹配问题)
Escape Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Subm ...
随机推荐
- 使用LogParser 将iis日志导入到数据库中
--注意修改日志位置和表名--u_ex180228.log 日志所在位置 --IISLog 表名--IISLog_table_0228 表名"C:\Program Files (x86)\L ...
- Algorithms - Bucket sort
印象 图1 将元素分布在桶中 图2 元素在每个桶中排序 思想 桶排序将数组分到有限数量的桶子里.每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序). 分析 时间复杂度: ...
- 将Winform程序及dll打包成可执行的exe
使用场景 通常开发的Winform程序,引用了其他类库后,在输出目录下都会产生很多DLL文件,exe执行时必须依赖这些DLL.想要Winform程序只有一个可执行exe文件,又不想打包成安装包,就可以 ...
- git常用命令(转)
git常用命令: git init //初始化本地git环境 git clone XXX//克隆一份代码到本地仓库 git pull //把远程库的代码更新到工作台 git pull --rebase ...
- python安装opencv库
1.打开anaconda prompt(安装anaconda会默认安装),键入 pip install opencv-python,如下: 2.安装过程如下所示: 3 测试是否安装成功 上述就说明安装 ...
- SnackBar使用
注意:build(BuildContext context)在 Scaffold.of(context)之前时,会报错,解决办法:通过build widget来解决,如下代码. import 'pac ...
- ubuntu - 14.10,安装uget和aria2(下载工具)
Windows下的下载工具--迅雷,之所以下载速度快,乃是它能搜索资源.为己所用,而不是仅仅从原始地址这单一资源处下载. Ubuntu下也有类似的工具,那就是aira2. aira2是一个命令行下载工 ...
- 算法训练 数字三角形(DP)
问题描述 (图3.1-1)示出了一个数字三角形. 请编一个程序计算从顶至底的某处的一条路 径,使该路径所经过的数字的总和最大. ●每一步可沿左斜线向下或右斜线向下走: ●1<三角形行数≤100: ...
- hdu1403(后缀数组模板)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1403 题意: 给出两个字符串, 求他们的最长公共子串 思路: 两个字符串的最长公共子串长度显然就是两个 ...
- jzoj5683. 【GDSOI2018模拟4.22】Prime (Min_25筛+拉格朗日插值+主席树)
题面 \(n\leq 10^{12},k\leq 100\) 题解 一眼就是一个\(Min\_25\)筛+拉格朗日插值优化,然而打完之后交上去发现只有\(60\)分 神\(tm\)还要用主席树优化-- ...