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, ...
随机推荐
- [日常] Go语言圣经--结构体,JSON习题
Go语言圣经-结构体 1.结构体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体 2.通常一行对应一个结构体成员,成员的名字在前类型在后,不过如果相邻的成员类型如果相同的话可以被合并到一行 ...
- 星系炸弹-2015省赛C语言A组第二题
星系炸弹 在X星系的广袤空间中漂浮着许多X星人造“炸弹”,用来作为宇宙中的路标.每个炸弹都可以设定多少天之后爆炸.比如:阿尔法炸弹2015年1月1日放置,定时为15天,则它在2015年1月16日爆炸. ...
- 自定义MVC框架之工具类-文件上传类
截止目前已经改造了3个类: ubuntu:通过封装验证码类库一步步安装php的gd扩展 自定义MVC框架之工具类-分页类的封装 该文件上传类功能如下: 1,允许定制上传的文件类型,文件mime信息,文 ...
- contenttypes组件 (处理大量外键)
介绍 Django contenttypes是一个非常有用的框架,主要用来创建模型间的通用关系(generic relation). https://www.cnblogs.com/huchong ...
- Java 并发:Executor ExecutorService ThreadPoolExecutor
Executor Executor仅仅是一个简单的接口,其定义如下 public interface Executor { void execute(Runnable command); } 作为一个 ...
- 用node.js模拟服务器和客户端
服务器 代码 var net = require("net") var server = net.createServer(); server.listen(12306," ...
- cf232E. Quick Tortoise(分治 bitset dp)
题意 题目链接 Sol 感觉这个思路还是不错的 #include<bits/stdc++.h> using namespace std; const int MAXN = 501, SS ...
- Linux 操作方法(一)
cd +地址 ->可进入地址 cd .. ->返回上级 ll ->显示当前目录下的所有文件(显示所有文件,包括隐藏,还有显示文件信息) ls ->显示当前目录下的所有文件 su ...
- AsyncTask GET请求
布局: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android= ...
- Android之在linux终端执行shell脚本直接打印当前运行app的日志
1.问题 我们一般很多时候会需要在ubuntu终端上打印当前运行app的日志,我们一般常见的做法是 1).获取包名 打开当前运行的app,然后输入如下命令,然后在第一行TASK后面的就可以看到包名 a ...