COGS 426. 血帆海盗 最小割定理

最小割定理:

最小割的必须边 
一定在最小割中的边、扩大容量后能增大最大流的边, ① 满流;② 残余网络中S能到入点、出
点能到T。 从S开始DFS、T开始反向DFS,标记到达的点,然后枚举满流边即可。
最小割的可行边 
被某一种最小割的方案包含的边, ① 满流;② 删掉之后在残余网络中找不到u到v的路
径。 在残余网络中tarjan求SCC,(u,v)两点在同一SCC中说明残余网络中存在u到v路径。

知道结论后就好做了。

首先S向小于n/2,小于n/2向大于n/2,大于n/2向T建容量为1的边,跑出最大匹配。

然后在网络残图中跑tarjan,如果最大流中的一条边起点和终点不在同一个强连通分量。

那么砍掉这条边就是合法的,如果起点和终点在同一个强连通分量,那么ans--。


#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#define inf 100000000
using namespace std;
struct bian
{
  int qi,zhong,w,next;
};
bian c[1000000];
int n,m,jishu=0,hd,tl,x,y,xuan,ying,jishu3=0,jishu2=0,ji=0;
int head[200000],dep[200000],q[200000],belong[200000],dfn[200000],low[200000],zhan[200000];
bool chu[200000],ru[200000],in[200000];
void add(int x,int y,int z)
{
  c[jishu].qi=x;
  c[jishu].zhong=y;
  c[jishu].w=z;
  c[jishu].next=head[x];
  head[x]=jishu++;
}
bool bfs()
{
  memset(dep,0,sizeof(dep));
  hd=tl=0;
  q[++tl]=xuan;
  dep[xuan]=1;
  while(hd<tl)
  {
    int op=q[++hd];
    for(int i=head[op];i!=-1;i=c[i].next)
    {
      if(c[i].w&&(!dep[c[i].zhong]))
      {
        dep[c[i].zhong]=dep[op]+1;
        q[++tl]=c[i].zhong;
        if(c[i].zhong==ying)
          return 1;
      }
    }
  }
  return 0;
}
int dfs(int op,int fw)
{
  if(op==ying)  return fw;
  int tmp=fw,k;
  for(int i=head[op];i!=-1;i=c[i].next)
  {
    if(c[i].w&&tmp&&dep[c[i].zhong]==dep[op]+1)
    {
      k=dfs(c[i].zhong,min(c[i].w,tmp));
      if(!k)
      {
        dep[c[i].zhong]=0;
        continue;
      }
      c[i].w-=k;
      c[i^1].w+=k;
      tmp-=k;
    }
  }
  return fw-tmp;
}
void tarjan(int x)
{
  low[x]=++jishu2;
  dfn[x]=jishu2;
  zhan[++jishu3]=x;
  in[x]=1;
  for(int i=head[x];i!=-1;i=c[i].next)
  {
  	if(!c[i].w)  continue;
    if(dfn[c[i].zhong]==-1)
    {
      tarjan(c[i].zhong);
      low[x]=min(low[x],low[c[i].zhong]);
    }
    else  if(in[c[i].zhong])  
		  low[x]=min(low[x],dfn[c[i].zhong]);
  }
  if(dfn[x]==low[x])
  {
    int y;
    ji++;
    while(1)
    {
      y=zhan[jishu3--];
      in[y]=0;
      belong[y]=ji;
      if(y==x)
        break;
    }
  }
}
int main()
{
	freopen("bloodsail.in","r",stdin);
  freopen("bloodsail.out","w",stdout);
	memset(head,-1,sizeof(head));
	memset(dfn,-1,sizeof(dfn));
  scanf("%d%d",&n,&m);
  xuan=0;ying=n+1;
  for(int i=1;i<=m;++i)
  {
    scanf("%d%d",&x,&y);
    if(!chu[x])
    {
      add(xuan,x,1);
      add(x,xuan,0);
      chu[x]=1;
    }
    if(!ru[y])
    {
		  add(y,ying,1);
		  add(ying,y,0);
	  	ru[y]=1;
	  }
    add(x,y,1);
    add(y,x,0);
  }
  int shu=0;
  while(bfs())
    shu+=dfs(0,inf);
  for(int i=1;i<=n;++i)
    if(dfn[i]==-1)
      tarjan(i);
  for(int i=1;i<=n/2;++i)
    for(int j=head[i];j!=-1;j=c[j].next)
      if(c[j].w==0&&c[j].zhong!=0&&belong[c[j].qi]==belong[c[j].zhong])
        shu--;
  printf("%d",shu);
  return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是将代码修改为cot平滑的方法: 1. 首先,需要使用边界角的cot权重计算每个顶点的权重。 2. 然后,使用cot权重对每个顶点的邻域点进行加权计算,得到平滑后的坐标。 3. 最后,根据平滑后的坐标更新每个顶点的位置。 修改后的代码如下: float smooth() { float err = -1; cogs.clear(); v_end = mesh.vertices_end(); //cot平滑 for (v_it = mesh.vertices_begin(); v_it != v_end; ++v_it) { cog[0] = cog[1] = cog[2] = weight_sum = 0.0; for (vv_it = mesh.vv_iter(*v_it); vv_it.is_valid(); ++vv_it) { double cot_weight = 0.0; MyMesh::HalfedgeHandle heh = mesh.find_halfedge(*v_it, *vv_it); if (!mesh.is_boundary(heh)) { MyMesh::HalfedgeHandle prev_heh = mesh.prev_halfedge_handle(heh); MyMesh::HalfedgeHandle next_heh = mesh.next_halfedge_handle(heh); MyMesh::VertexHandle prev_vh = mesh.to_vertex_handle(prev_heh); MyMesh::VertexHandle next_vh = mesh.to_vertex_handle(next_heh); MyMesh::Point prev_p = mesh.point(prev_vh); MyMesh::Point curr_p = mesh.point(*v_it); MyMesh::Point next_p = mesh.point(next_vh); double cot_alpha = cot(prev_p - curr_p, next_p - curr_p); double cot_beta = cot(curr_p - prev_p, next_p - prev_p); cot_weight = cot_alpha + cot_beta; } cog += cot_weight * mesh.point(*vv_it); weight_sum += cot_weight; } cogs.push_back(cog / weight_sum); } for (v_it = mesh.vertices_begin(), cog_it = cogs.begin(); v_it != v_end; ++v_it, ++cog_it) { if (!mesh.is_boundary(*v_it)) { MyMesh::Point p = mesh.point(*v_it); err = max(err, (p - *cog_it).norm()); mesh.set_point(*v_it, *cog_it); } } return err; } 其中cot函数的定义如下: double cot(MyMesh::Point a, MyMesh::Point b) { return dot(a, b) / cross(a, b).norm(); } 注意,这里使用的是边界角的cot权重,因此在计算cot权重时需要判断当前边是否为边界。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值