BZOJ2893:征服王(费用流)
Description
Input
Output
Sample Input
2 1 1 1
1
2
2 1
3 2 3 3
1 2 3
1 2 3
1 2
1 3
Sample Output
2
【数据规模和约定】
对于30%的数据,满足n <= 10, m <= 100。
对于60%的数据,满足n <= 200, m <= 5000。
对于100%的数据,满足t<=10,n <= 1000, m <= 10000。
Solution
打死白学家
首先第一感觉这个题很像最小路径覆盖……但他并不是一个$DAG$。所以我们自己动手丰衣足食把它缩成一个$DAG$。
现在变成了一个多起点多终点的最小路径覆盖问题。我们首先把一个点拆成$u$和$u'$,并且$u$连向$u'$一条流量为$1$,费用为1的边。
然后原图的边就$u'$连向$v$一条流量为$INF$,费用为$0$的边。
源点$S$向图中的起点$s$连流量为$INF$,费用为$0$的边,终点$t'$向图中的汇点$E$连流量为$INF$,费用为$0$的边。
跑一边最大费用最大流。如果费用为点数那么说明每个点都经过了,答案为增广次数。
否则无解。
Code
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define N (10009)
using namespace std; struct Edge{int to,next,flow,cost;}edge[N*];
int DFN[N],Low[N],Stack[N],ID[N],Vis[N],top,dfs_num,id_num;
int T,n,m,A,B,a[N],b[N],u[N*],v[N*],x,INF,s,e=;
int dis[N],pre[N],vis[N];
int head[N],num_edge;
queue<int>q; void Clear()
{
memset(head,,sizeof(head));
memset(DFN,,sizeof(DFN));
memset(Low,,sizeof(Low));
memset(ID,,sizeof(ID));
memset(Vis,,sizeof(Vis));
num_edge=top=dfs_num=id_num=;
} void add(int u,int v,int l,int c)
{
edge[++num_edge].to=v;
edge[num_edge].next=head[u];
edge[num_edge].flow=l;
edge[num_edge].cost=c;
head[u]=num_edge;
} bool SPFA(int s,int e)
{
memset(dis,0x7f,sizeof(dis));
memset(pre,-,sizeof(pre));
dis[s]=; q.push(s); vis[s]=;
while (!q.empty())
{
int x=q.front(); q.pop();
for (int i=head[x]; i; i=edge[i].next)
if (edge[i].flow && dis[x]+edge[i].cost<dis[edge[i].to])
{
dis[edge[i].to]=dis[x]+edge[i].cost;
pre[edge[i].to]=i;
if (!vis[edge[i].to])
{
vis[edge[i].to]=;
q.push(edge[i].to);
}
}
vis[x]=;
}
return dis[e]!=INF;
} void MCMF(int s,int e)
{
int fee=,ans=;
while (SPFA(s,e))
{
if (dis[e]==) break;
else ans++;
int d=INF;
for (int i=e; i!=s; i=edge[((pre[i]-)^)+].to)
d=min(d,edge[pre[i]].flow);
for (int i=e; i!=s; i=edge[((pre[i]-)^)+].to)
{
edge[pre[i]].flow-=d;
edge[((pre[i]-)^)+].flow+=d;
}
fee+=d*dis[e];
}
if (-fee==id_num) printf("%d\n",ans);
else puts("no solution");
} void Tarjan(int x)
{
DFN[x]=Low[x]=++dfs_num;
Stack[++top]=x; Vis[x]=;
for (int i=head[x]; i; i=edge[i].next)
if (!DFN[edge[i].to])
{
Tarjan(edge[i].to);
Low[x]=min(Low[x],Low[edge[i].to]);
}
else if (Vis[edge[i].to])
Low[x]=min(Low[x],DFN[edge[i].to]);
if (DFN[x]==Low[x])
{
ID[x]=++id_num; Vis[x]=;
while (Stack[top]!=x)
{
ID[Stack[top]]=id_num;
Vis[Stack[top--]]=;
}
top--;
}
} int main()
{
memset(&INF,0x7f,sizeof(INF));
scanf("%d",&T);
while (T--)
{
Clear();
scanf("%d%d%d%d",&n,&m,&A,&B);
for (int i=; i<=A; ++i) scanf("%d",&a[i]);
for (int i=; i<=B; ++i) scanf("%d",&b[i]);
for (int i=; i<=m; ++i) scanf("%d%d",&u[i],&v[i]), add(u[i],v[i],,);
for (int i=; i<=n; ++i) if (!DFN[i]) Tarjan(i);
memset(head,,sizeof(head)); num_edge=;
for (int i=; i<=A; ++i)
add(s,ID[a[i]],INF,), add(ID[a[i]],s,,);
for (int i=; i<=B; ++i)
add(ID[b[i]]+,e,INF,), add(e,ID[b[i]]+,,);
for (int i=; i<=m; ++i)
if (ID[u[i]]!=ID[v[i]])
add(ID[u[i]]+,ID[v[i]],INF,), add(ID[v[i]],ID[u[i]]+,,);
for (int i=; i<=id_num; ++i)
{
add(i,i+,,-); add(i+,i,,);
add(i,i+,INF,); add(i+,i,,);
}
MCMF(s,e);
}
}
BZOJ2893:征服王(费用流)的更多相关文章
- BZOJ2893: 征服王
题解: 裸的上下界最小流是有问题的.因为在添加了附加源之后求出来的流,因为s,t以及其它点地位都是平等的.如果有一个流经过了s和t,那么总可以认为这个流是从s出发到t的满足题意的流. 既然可能存在s到 ...
- 【BZOJ-2893】征服王 最大费用最大流(带下界最小流)
2893: 征服王 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 156 Solved: 48[Submit][Status][Discuss] D ...
- bzoj2893(费用流)
先缩点,然后拆点,其实是很经典的一种操作,把不好做的点拆成边,然后我一开始想的是网络流,答案当然是增广次数, 但可以发现跑网络流的话不同的跑法增广次数不一样,不太好找最小的.我们可以换一种神奇的思路, ...
- hdu-5988 Coding Contest(费用流)
题目链接: Coding Contest Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Ot ...
- POJ2195 Going Home[费用流|二分图最大权匹配]
Going Home Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 22088 Accepted: 11155 Desc ...
- BZOJ3130: [Sdoi2013]费用流[最大流 实数二分]
3130: [Sdoi2013]费用流 Time Limit: 10 Sec Memory Limit: 128 MBSec Special JudgeSubmit: 960 Solved: 5 ...
- 洛谷 1004 dp或最大费用流
思路: dp方法: 设dp[i][j][k][l]为两条没有交叉的路径分别走到(i,j)和(k,l)处最大价值. 则转移方程为 dp[i][j][k][l]=max(dp[i-1][j][k-1][l ...
- Codeforces 730I [费用流]
/* 不要低头,不要放弃,不要气馁,不要慌张 题意: 给两行n个数,要求从第一行选取a个数,第二行选取b个数使得这些数加起来和最大. 限制条件是第一行选取了某个数的条件下,第二行不能选取对应位置的数. ...
- zkw费用流+当前弧优化
zkw费用流+当前弧优化 var o,v:..] of boolean; f,s,d,dis:..] of longint; next,p,c,w:..] of longint; i,j,k,l,y, ...
随机推荐
- 实现泛型数组的冒泡排序算法 C#
public static class BubbleSortTool { public static void BubbleSort<T>(this T[] array, Ascendin ...
- PowerDesigner Constraint name uniqueness 错误
使用PowerDesigner生成数据库脚本时报 Constraint name uniqueness 错误: 双击每行错误,发现外键引用的名字有重复的: 惯性去网上找解决办法,找到的主要是两个方法: ...
- 过滤器模式(Filter Pattern)
过滤器模式 一.什么是过滤器模式 过滤器模式(Filter Pattern),这种模式允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来.这种类型的设计模式属于结构型 ...
- oracle逐步学习总结之权限和角色(基础六)
原创作品,转自请注明出处:https://www.cnblogs.com/sunshine5683/p/10236129.html 继续上节的索引,这次主要总结oracle数据库的权限问题!(在总结的 ...
- Android 7.0 以上保留数据去除锁屏密码的方法
在很多情况下,由于我们的疏忽,导致忘记了锁屏密码.也没有开启 USB 调试,内置存储中还有要保留的数据.我们需要在不清除数据的情况下删除锁屏密码.接下来我们就介绍该如何做. 注意事项 首先要注意,这个 ...
- Android的Intent你知道多少?
https://blog.csdn.net/sinat_34383316/article/details/76039483 https://blog.csdn.net/u014492609/artic ...
- Codeforces852G(字符串hash)
G. Bathroom terminal time limit per test:2 seconds memory limit per test:256 megabytes input:standar ...
- JS 模块 p6
利用了闭包的模块: 简单模块例子: function fn(){ ; function y(){ console.log(x); } return { y:y} }var do1 = fn() do1 ...
- ubuntn中root密码设置和权限给予
个人理解: 1.安装ubuntn,会首先要求有一个账号密码设置,比如我设置的:用户名irt,密码**,这是一个普通账户,此时,超级管理账户root密码是空的: 2.给root账户设置密码,大概步骤见下 ...
- 关于TCP/IOCP构架中出现的假死连接解决方案
如果在2台不同的公网机器,对TCP的c/s做过详细的压力测试,那么很不幸,会有很多人发现自己的server端会出现大量的假死连接. 假死连接具体表现如下: 1.在s端机器上,会有一些处于TCP_EST ...