您的位置:首页 > 理论基础 > 计算机网络

UVA 11419 SAM I AM 网络流

2017-08-15 14:59 211 查看
今天是在家待的最后一天了……在家的15天一直没有写过博客,今天还是写一篇吧。

思路:

这道题我用的是网络流。(好吧其实是因为不会KM算法)

用网络流做二分图的基本定理:

最大点权独立=总点权-最小点权覆盖

最大点权独立集:点和点之间没有边相连接

最小点权覆盖集:每一条边都有一个点被点亮,所有点的点权之和最小

这道题可转换为求最小点权覆盖集,也就是求最小割

怎么建图是我看了别人的,二分图的X集对应行号,Y集对应列号,把每个敌人的行号和列号相连。

之后求答案的方式很简单,跑个最小割即可。但我又不知道怎么输出割的是哪几条边,所以再次看了别人的。

做法详见代码。

以下是关于此做法(get_ans)的感性认知:

如果是这样

——1—

S—-2—(123汇集于5)5—-T

——3—

(假设S—1边满流)

很明显我们要割5—T这条边。在get_ans时我们可以通过一条未满流的边到达5,因此2和3都会被访问。

那么1如何被访问呢?

当访问到5时 1->5的反向边cap=0 flow>0 满足条件,返回访问1。

如果是这样

—–2—-

S—1 —–3—-(2 3 4汇集与T)

—–4—-

那么1肯定满流 访问不了,因此2 3 4也访问不了 。

注意:

不能再用w[i]的写法了,应用cap[i]和flow[i]

注意flow[i]和w[i]的加减变化刚好相反,这点也比较容易理解

flow流量 既然流过了 自然要加 反向边当然要减

而w可理解为还有多少流量可以走 流过自然要减

#include<cstdio>
#include<cstring>
#include<queue>
#define INF 10000000
using namespace std;
const int P=2500,E=2008106;
int r,c,n;
int T,S;
int to[E],nxt[E],cap[E],flow[E],head[P],etot;
int dis[P];
bool vis[P],foot[P];
void adde(int u,int v,int c)
{
to[++etot]=v;
nxt[etot]=head[u];
cap[etot]=c;
head[u]=etot;
}
void ins(int u,int v,int c)
{
adde(u,v,c);
adde(v,u,0);
}
bool bfs()
{
memset(dis,-1,sizeof(dis));
queue<int> q;
q.push(S);
dis[0]=0;
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(cap[i]-flow[i]>0&&dis[v]==-1){
dis[v]=dis[u]+1;
q.push(v);
}
}
}
return dis[T]==-1?0:1;
}
int dfs(int u,int a)
{
if(u==T||a==0) return a;
int used=0,f;
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(dis[v]==dis[u]+1&&cap[i]-flow[i]>0){
f=dfs(v,min(a-used,cap[i]-flow[i]));
used+=f;
flow[i]+=f;flow[i^1]-=f;//!!
if(used==a) return used;
}
}
if(!used) dis[u]=-1;
return used;
}
void dinic()
{
int ans=0;
while(bfs()){
ans+=dfs(S,INF);
}
printf("%d ",ans);
}
void get_ans(int u)
{
foot[u]=1;
for(int i=head[u];i;i=nxt[i])
if(cap[i]-flow[i]>0&&!foot[to[i]])
get_ans(to[i]);

}
void init()
{
etot=1;
memset(vis,0,sizeof(vis));
memset(head,0,sizeof(head));
memset(foot,0,sizeof(foot));
memset(cap,0,sizeof(cap));
memset(flow,0,sizeof(flow));
}
int main()
{
while(scanf("%d%d%d",&r,&c,&n)&&(n||c||r)){
init();
T=r+c+1,S=0;
for(int i=1;i<=n;i++){
int x,y;
scanf("%d%d",&x,&y);
ins(x,r+y,INF);
if(!vis[x]){
ins(S,x,1);
vis[x]=1;
}
if(!vis[r+y]){
ins(r+y,T,1);
vis[r+y]=1;
}
}
dinic();
get_ans(S);
for(int i=1;i<=r;i++)
if(vis[i]&&!foot[i]) printf("r%d ",i);
for(int i=r+1;i<=r+c;i++)
if(vis[i]&&foot[i]) printf("c%d ",i-r);
printf("\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: