方法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. 淘宝(阿里百川)手机客户端开发日记第六篇 Service详解(四)

    DEMO1:在Activity里声明一个回调方法,当service完成任务后,调用这个回调方法. 首先,我们先继承service,来创建服务,代码如下: package com.example.ser ...

  2. HDU 2577 How to Type(dp题)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2577 解题报告:有一个长度在100以内的字符串,并且这个字符串只有大写和小写字母组成,现在要把这些字符 ...

  3. Pascal’s Triangle

    vector<vector<int>> generate(int num) { vector<vector<int>> result; vector&l ...

  4. OpenWRT

    如何查日志:http://blog.appdevp.com/archives/382 logread OpenWrt下创建crontab任务: 确保/etc/crontab/下有名为"roo ...

  5. sqltext的参数化处理

    说到sql的参数化处理,我也是醉了,因为sql引擎真的是一个无比强大的系统,我们平时做系统的时候都会加上缓存,我想如果没有缓存,就不会有什么 大网站能跑的起来,而且大公司一般会在一个东西上做的比较用心 ...

  6. Java for LeetCode 039 Combination Sum

    Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C wher ...

  7. Gym 100801E Easy Arithmetic (思维题)

    题目:传送门.(需要下载PDF) 题意:给定一个长度不超过1000的字符串表达式,向该表达式中加入'+'或'-',使得表达式的值最大,输出该表达式. 题解:比如300-456就改成300-4+56,遇 ...

  8. CodeForces - 426A(排序)

    Sereja and Mugs Time Limit: 1000MS   Memory Limit: 262144KB   64bit IO Format: %I64d & %I64u Sub ...

  9. Mac OS Ruby安装 使用RVM

    访问http://www.ruby-lang.org/en/downloads/ 使用第三方工具安装ruby,经过了解,在mac下可以使用macports和rvm安装ruby. 经过实际操作觉得rvm ...

  10. IncDec Sequence(codevs 2098)

    题目描述 Description 给定一个长度为n的数列{a1,a2...an},每次可以选择一个区间[l,r],使这个区间内的数都加一或者都减一. 问至少需要多少次操作才能使数列中的所有数都一样,并 ...