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, ...
随机推荐
- git 使用 VisualStudio 比较分支更改
有时候需要比较两个分支的不同,这时如果提交到 github ,那么默认就可以看到.但是这时因为没有ide的高亮或者其他的功能,看起来觉得不好 默认的 VisualStudio 比较文件比 github ...
- winform窗体 小程序【进程】
进程 一个应用程序就是一个进程,我的理解是,只要是打开应用程序,就会创建进程. 在.NET框架在using.System.Diagnostics名称空间中,有一个类Process,用来创建一个新的进程 ...
- 什么是Solr
什么是Solr Lucene复习: 1.什么是lucene:全文检索工具包 2.Lucene的工作原理: 索引数据的创建 从原始文件中提取一些可以用来搜索的数据(封装成各种Field),把各field ...
- Codeforces500C(SummerTrainingDay01-G)
C. New Year Book Reading time limit per test:2 seconds memory limit per test:256 megabytes input:sta ...
- POJ3468(KB7-C 线段树)
A Simple Problem with Integers Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 108903 ...
- 用01随机函数构造[a,b]整数范围随机数
#include <stdio.h> #include <stdlib.h> #define RAND_0_1 (rand()&0x1) int random(int ...
- python学习之老男孩python全栈第九期_day007作业
一.关系运算 有如下两个集合,pythons是报名python课程的学员名字集合,linuxs是报名linux课程的学员名字集合pythons={'alex','egon','yuanhao','wu ...
- Java学习笔记之异常处理
一.异常的分类 1.由Java虚拟机抛出的异常(Error):程序无法处理的问题,用户不用去进行处理(虚拟机错误丶内存溢出错误丶线程死锁) 2.Exception异常:程序本身可以进行处理的异常 1. ...
- Expo大作战(十三)--expo如何自定义状态了statusBar以及expo中如何处理脱机缓存加载 offline support
简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...
- 移动端采坑:Position: fixed 在Safari上的Bug
Position: fixed 在IOS上的显示效果 会出现两种情况: 点击fixed定位的元素会出现fixed定位失效导致的元素贴向底部,即position: absolute,bottom: 0p ...