方法1:两遍最大流。一遍最大流后,把满流边容量+1,非满流边改为INF;再求最小割即为答案。

我大概想了下证明:能构成最小割的边在第一次跑最大流时都满流,然后按那样改变边容量再求一次最小割,就相当于再在那些 满流可能是属于最小割的边 中挑出最少的边形成ST割。

 #include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
#define INF (1LL<<60)
#define MAXN 1111
#define MAXM 440000 struct Edge{
int v,next;
__int64 cap,flow;
}edge[MAXM];
int vs,vt,NE,NV;
int head[MAXN]; void addEdge(int u,int v,__int64 cap){
edge[NE].v=v; edge[NE].cap=cap; edge[NE].flow=;
edge[NE].next=head[u]; head[u]=NE++;
edge[NE].v=u; edge[NE].cap=; edge[NE].flow=;
edge[NE].next=head[v]; head[v]=NE++;
} int level[MAXN];
int gap[MAXN];
void bfs(){
memset(level,-,sizeof(level));
memset(gap,,sizeof(gap));
level[vt]=;
gap[level[vt]]++;
queue<int> que;
que.push(vt);
while(!que.empty()){
int u=que.front(); que.pop();
for(int i=head[u]; i!=-; i=edge[i].next){
int v=edge[i].v;
if(level[v]!=-) continue;
level[v]=level[u]+;
gap[level[v]]++;
que.push(v);
}
}
} int pre[MAXN];
int cur[MAXN];
__int64 ISAP(){
bfs();
memset(pre,-,sizeof(pre));
memcpy(cur,head,sizeof(head));
int u=pre[vs]=vs;
__int64 flow=,aug=INF;
gap[]=NV;
while(level[vs]<NV){
bool flag=false;
for(int &i=cur[u]; i!=-; i=edge[i].next){
int v=edge[i].v;
if(edge[i].cap!=edge[i].flow && level[u]==level[v]+){
flag=true;
pre[v]=u;
u=v;
//aug=(aug==-1?edge[i].cap:min(aug,edge[i].cap));
aug=min(aug,edge[i].cap-edge[i].flow);
if(v==vt){
flow+=aug;
for(u=pre[v]; v!=vs; v=u,u=pre[u]){
edge[cur[u]].flow+=aug;
edge[cur[u]^].flow-=aug;
}
//aug=-1;
aug=INF;
}
break;
}
}
if(flag) continue;
int minlevel=NV;
for(int i=head[u]; i!=-; i=edge[i].next){
int v=edge[i].v;
if(edge[i].cap!=edge[i].flow && level[v]<minlevel){
minlevel=level[v];
cur[u]=i;
}
}
if(--gap[level[u]]==) break;
level[u]=minlevel+;
gap[level[u]]++;
u=pre[u];
}
return flow;
}
int main(){
int t,n,m,a,b,c,d;
scanf("%d",&t);
for(int cse=; cse<=t; ++cse){
scanf("%d%d",&n,&m);
vs=; vt=n-; NV=n; NE=;
memset(head,-,sizeof(head));
while(m--){
scanf("%d%d%d%d",&a,&b,&c,&d);
if(d){
addEdge(a,b,c);
addEdge(b,a,c);
}else{
addEdge(a,b,c);
}
}
ISAP();
for(int i=; i<NE; i+=){
if(edge[i].flow==edge[i].cap) ++edge[i].cap;
else edge[i].cap=INF;
}
printf("Case %d: %I64d\n",cse,ISAP());
}
return ;
}

方法2:放大边权。把每条边的容量cap改为cap*m+1,m为一个够大的数(总边数+1就够了)。最后跑一遍最大流,最小割为maxflow/m,最少边数为maxflow%m。

我大概想了下证明:原图流量能满的,新图流量肯定也能达到cap*m,因为可以把新图看成是m张原图。而流量达到cap*m的边还剩1这个容量还没用到(流量达不到cap*m自然就用不到这个1容量了),那么就相当于在这些边的基础上再跑一遍最大流,与方法1差不多的道理。

 #include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
#define INF (1LL<<60)
#define MAXN 1111
#define MAXM 440000 struct Edge{
int v,next;
__int64 cap,flow;
}edge[MAXM];
int vs,vt,NE,NV;
int head[MAXN]; void addEdge(int u,int v,__int64 cap){
edge[NE].v=v; edge[NE].cap=cap; edge[NE].flow=;
edge[NE].next=head[u]; head[u]=NE++;
edge[NE].v=u; edge[NE].cap=; edge[NE].flow=;
edge[NE].next=head[v]; head[v]=NE++;
} int level[MAXN];
int gap[MAXN];
void bfs(){
memset(level,-,sizeof(level));
memset(gap,,sizeof(gap));
level[vt]=;
gap[level[vt]]++;
queue<int> que;
que.push(vt);
while(!que.empty()){
int u=que.front(); que.pop();
for(int i=head[u]; i!=-; i=edge[i].next){
int v=edge[i].v;
if(level[v]!=-) continue;
level[v]=level[u]+;
gap[level[v]]++;
que.push(v);
}
}
} int pre[MAXN];
int cur[MAXN];
__int64 ISAP(){
bfs();
memset(pre,-,sizeof(pre));
memcpy(cur,head,sizeof(head));
int u=pre[vs]=vs;
__int64 flow=,aug=INF;
gap[]=NV;
while(level[vs]<NV){
bool flag=false;
for(int &i=cur[u]; i!=-; i=edge[i].next){
int v=edge[i].v;
if(edge[i].cap!=edge[i].flow && level[u]==level[v]+){
flag=true;
pre[v]=u;
u=v;
//aug=(aug==-1?edge[i].cap:min(aug,edge[i].cap));
aug=min(aug,edge[i].cap-edge[i].flow);
if(v==vt){
flow+=aug;
for(u=pre[v]; v!=vs; v=u,u=pre[u]){
edge[cur[u]].flow+=aug;
edge[cur[u]^].flow-=aug;
}
//aug=-1;
aug=INF;
}
break;
}
}
if(flag) continue;
int minlevel=NV;
for(int i=head[u]; i!=-; i=edge[i].next){
int v=edge[i].v;
if(edge[i].cap!=edge[i].flow && level[v]<minlevel){
minlevel=level[v];
cur[u]=i;
}
}
if(--gap[level[u]]==) break;
level[u]=minlevel+;
gap[level[u]]++;
u=pre[u];
}
return flow;
}
int main(){
int t,n,m,a,b,c,d;
scanf("%d",&t);
for(int cse=; cse<=t; ++cse){
scanf("%d%d",&n,&m);
vs=; vt=n-; NV=n; NE=;
memset(head,-,sizeof(head));
__int64 x=m<<|;
while(m--){
scanf("%d%d%d%d",&a,&b,&c,&d);
if(d){
addEdge(a,b,c*x+);
addEdge(b,a,c*x+);
}else{
addEdge(a,b,c*x+);
}
}
printf("Case %d: %I64d\n",cse,ISAP()%x);
}
return ;
}

HDU3987 Harry Potter and the Forbidden Forest(边数最少的最小割)的更多相关文章

  1. hdu 3987 Harry Potter and the Forbidden Forest 求割边最少的最小割

    view code//hdu 3987 #include <iostream> #include <cstdio> #include <algorithm> #in ...

  2. HDU 3987 Harry Potter and the Forbidden Forest(边权放大法+最小割)

    Harry Potter and the Forbidden Forest Time Limit: 5000/3000 MS (Java/Others)    Memory Limit: 65536/ ...

  3. 【hdu 3987】Harry Potter and the Forbidden Forest

    [Link]:http://acm.hdu.edu.cn/showproblem.php?pid=3987 [Description] 给出一张有n个点的图,有的边又向,有的边无向,现在要你破坏一些路 ...

  4. HDU3987(最小割最少割边)

    Harry Potter and the Forbidden Forest Time Limit: 5000/3000 MS (Java/Others)    Memory Limit: 65536/ ...

  5. hdu3987,最小割时求最少割边数

    题意:求最小割时候割边最少的数量.算法:先求dinic一遍,跑出残网络,再把该网络中满流量(残量为0)的边 残量改为1,其他边残量改为无穷,则再跑一次最大流,所得即为答案.(思,最小割有喝多组,但是要 ...

  6. 最大流&最小割 - 专题练习

    [例1][hdu5889] - 算法结合(BFS+Dinic) 题意 \(N\)个点\(M\)条路径,每条路径长度为\(1\),敌人从\(M\)节点点要进攻\(1\)节点,敌人总是选择最优路径即最短路 ...

  7. 【转】最短路&差分约束题集

    转自:http://blog.csdn.net/shahdza/article/details/7779273 最短路 [HDU] 1548 A strange lift基础最短路(或bfs)★254 ...

  8. 【转载】图论 500题——主要为hdu/poj/zoj

    转自——http://blog.csdn.net/qwe20060514/article/details/8112550 =============================以下是最小生成树+并 ...

  9. 【HDOJ图论题集】【转】

    =============================以下是最小生成树+并查集====================================== [HDU] How Many Table ...

随机推荐

  1. Unity 3D学习之 Prime31 Game Center插件用法

    http://momowing.diandian.com/post/2012-11-08/40041806328 It's my life~: 为app 连入Game Center 功能而困扰的朋友们 ...

  2. poj1611(感染病患者)

    The Suspects Time Limit: 1000MS   Memory Limit: 20000K Total Submissions: 24587   Accepted: 12046 De ...

  3. HM必修1

    高中数学必修一 笔记与拓展 1. 集合与函数概念 集合概念 集合是一个基本的数学概念. 集合是由集合的元素构成的. 当且仅当两个集合中包含着完全相同的元素且都不包含其它元素时两个集合相等. 集合是确定 ...

  4. 用Matplotlib绘制二维图像

    唠叨几句: 近期在做数据分析,需要对数据做可视化处理,也就是画图,一般是用Matlib来做,但Matlib安装文件太大,不太想直接用它,据说其代码运行效率也很低,在网上看到可以先用Java做数据处理, ...

  5. 二级域名session 共享方案

    二级域名session 共享方案   1.利用COOKIE存放session_id(); 实例: 域名一文件php代码: <?php session_start(); setcookie(&qu ...

  6. Moebius集群:SQL Server一站式数据平台

    一.Moebius集群的架构及原理 1.无共享磁盘架构 Moebius集群采用无共享磁盘架构设计,各个机器可以不连接一个共享的设备,数据可以存储在每个机器自己的存储介质中.这样每个机器就不需要硬件上的 ...

  7. 每天一个命令day1【diff 命令】(具体实例看下一节)

    diff 命令是 linux上非常重要的工具,用于比较文件的内容,特别是比较两个版本不同的文件以找到改动的地方.diff在命令行中打印每一个行的改动.最新版本的diff还支持二进制文件.diff程序的 ...

  8. ubuntu14.04安装dropbox

    官网地址: https://www.dropbox.com/install?os=lnx 自己的系统如果没有设置全局翻(qiang)代理,使用deb文件安装后不能直接使用,因为还需要到官网安装prop ...

  9. asp.net文本编辑器FCKeditor使用方法详解

    文本编辑器的使用: 1.FCKeditor的官方网站是:http://www.fckeditor.net/download  目前最新的FCKeditor.Net_2.6.9版本. 请在此页下载:ht ...

  10. 【转】Solr从数据库导入数据(DIH)

    本文转自:http://blog.csdn.net/xiaoyu714543065/article/details/11849115 一. 数据导入(DataImportHandler-DIH) DI ...