POJ 2125 最小点权覆盖集(输出方案)

题意:给一个图(有自回路,重边),要去掉所有边,规则:对某个点,可以有2种操作:去掉进入该点
的所有边,也可以去掉出该点所有边,(第一种代价为w+,第二种代价为w-)。求最小代价去除所有边。


己思:点的权被分为出入?必然拆点啊!每个点一分为二,点权:出的为出点权,入的为入点权,原来边仍在
,注意,这里(1-)->(1+),不多添加边,因为相当于求新图的最小点权覆盖集(覆盖所有边,每选一个点
恰好去除了它的边(要原图的入边或出边),),所以不可以在新图上再添加边(这里不是到不到的问题了)
,若添加边i-->i+,最小割可能发生变化。所以求二分图最小割问题,=新添s,t的最大流。
这题要输出方案,即最小点权覆盖集的点,即最小割(该二分图为简单割),遍历边,若该边俩头

vis[]值(dinic后)不同,(i%2==0)必然为最小割边。

ps:神题??

#include<iostream>  //110ms ,1A
#include<queue>
#include<cstdio>
#include<vector>
using namespace std;
int n,m; int e[30000][3];int head[208];int nume=0;
const int inf=0x3f3f3f3f;
void addedge(int f,int l,int w)
{
    e[nume][0]=l;e[nume][1]=head[f];head[f]=nume;
    e[nume++][2]=w;
    e[nume][0]=f;e[nume][1]=head[l];head[l]=nume;
    e[nume++][2]=0;
}
int vis[205];int level[205];
bool bfs()
{
    for(int i=0;i<=2*n+1;i++)
        vis[i]=level[i]=0;

    vis[0]=1;queue<int>q;q.push(0);
    while(!q.empty())
    {
        int cur=q.front();q.pop();
        for(int i=head[cur];i!=-1;i=e[i][1])
        {     int v=e[i][0];
            if(!vis[v]&&e[i][2]>0)
              {
                  level[v]=level[cur]+1;
                  if(v==n*2+1)return 1;
                  vis[v]=1;
                  q.push(v);
              }
        }
    }
    return vis[n*2+1];
}
int dfs(int u,int minf)
{
    if(minf==0||u==n*2+1)return minf;
    int sumf=0,f;
    for(int i=head[u];i!=-1&&minf;i=e[i][1])
    {    int v=e[i][0];
        if(level[v]==level[u]+1&&e[i][2]>0)
        {
            f=dfs(v,minf<e[i][2]?minf:e[i][2]);
            e[i][2]-=f;e[i^1][2]+=f;
            sumf+=f;minf-=f;
        }
    }
    return sumf;
}
int dinic()
{
    int sum=0;
    while(bfs())
    sum+=dfs(0,inf);
    return sum;
}
int main()
{
    scanf("%d%d",&n,&m);         //建图
    int temp,ff,ll;    nume=0;
    for(int i=0;i<n*2+1;i++)
        head[i]=-1;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&temp);
        addedge(i+n,n*2+1,temp);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&temp);
        addedge(0,i,temp);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&ff,&ll);
        addedge(ff,ll+n,inf);
    }
    int ans=dinic();
    printf("%d\n",ans);
    int count=0;
    vector<int>cut1,cut2;       //  找方案,
    for(int i=0;i<=2*n+1;i++)
       for(int j=head[i];j!=-1;j=e[j][1])
       {
           if(j%2==0&&e[j][2]==0&&vis[i]==1&&vis[e[j][0]]==0)  //最小割边
              {
                  count++;
                    if(i==0)
                   cut1.push_back(e[j][0]);
                   else if(e[j][0]==2*n+1)
                     cut2.push_back(i-n);
              }
       }
       printf("%d\n",count);
       for(int i=0;i<cut1.size();i++)
         printf("%d -\n",cut1[i]);
       for(int i=0;i<cut2.size();i++)
         printf("%d +\n",cut2[i]);
    return 0;
}


转载于:https://www.cnblogs.com/yezekun/p/3925797.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值