【题意】
给出一个有向图,定义两种操作:w+(i)删掉i点的所有入边w-(i)删掉i点的所有出边,每种
操作都有对应的花费,问删掉所有边的最小花费。
【解答】
我基于的想法是对于每个点,分拆成ai和bi,ai表示入度,bi表示出度。根据点度数的条件
建图,后来我发现我的建图方法和人家的最小点覆盖的思路是一样的!我猛然发现最小点
覆盖的思路更好理解。同时,我还看到了人家的有趣的分析:
画出邻接矩阵后,模型就变成了,有两种武器,分别可以覆盖一行或者一列,就变成了
和poj3308一样。(值得借鉴的思路)
#include <iostream>
using namespace std;
const int oo=99999999,maxn=500,maxm=50000;
struct edge
{
int x,y,f,next,op;
}e[maxm];
int h[maxn],d[maxn],p[maxn],now[maxn],num[maxn];
bool v[maxn];
int n,m,s,t,tot;
void ins(int x,int y,int f)
{
e[++tot].x=x;e[tot].y=y;
e[tot].next=h[x];e[tot].f=f;
h[x]=tot;
e[++tot].x=y;e[tot].y=x;
e[tot].next=h[y];e[tot].f=0;
h[y]=tot;
e[tot].op=tot-1;e[tot-1].op=tot;
}
int isap()
{
int flow=0,aug=oo,u,v,tmp,i,j,ff;
for (i=0;i<=t;i++)
{
d[i]=0;p[i]=-1;
num[i]=0;now[i]=h[i];
}
num[0]=t+1;u=s;
while (d[s]<t+1)
{
for (ff=0,i=now[u];i;i=e[i].next)
{
v=e[i].y;
if (e[i].f && d[u]==d[v]+1)
{
ff=1;
if (e[i].f<aug) aug=e[i].f;
p[v]=i;now[u]=i;
u=v;
if (u==t)
{
flow+=aug;
while (u!=s)
{
j=p[u];
e[j].f-=aug;
e[e[j].op].f+=aug;
u=e[j].x;
}
aug=oo;
}
break;
}
}
if (ff) continue;
num[d[u]]--;
if (!num[d[u]]) return flow;
tmp=t+1;
for (i=h[u];i;i=e[i].next)
{
v=e[i].y;
if (e[i].f && d[v]<tmp)
{
tmp=d[v];now[u]=i;
}
}
d[u]=tmp+1;
num[d[u]]++;
if (u!=s) u=e[p[u]].x;
}
return flow;
}
void dfs(int x)
{
v[x]=true;
for (int i=h[x];i;i=e[i].next)
if (e[i].f && !v[e[i].y])
dfs(e[i].y);
}
int main()
{
freopen("pin.txt","r",stdin);
freopen("pou.txt","w",stdout);
int i,x,y,ans;
cin >> n >> m;
s=0;t=n+n+1;
for (i=1;i<=n;i++)
{
cin >> x;
ins(i+n,t,x);
}
for (i=1;i<=n;i++)
{
cin >> x;
ins(s,i,x);
}
for (i=0;i<m;i++)
{
cin >> x >> y;
ins(x,y+n,oo);
}
cout << isap() << endl;
memset(v,0,sizeof(v));
dfs(s);
ans=0;
for (i=1;i<=n;i++)
ans+=(!v[i])+(v[i+n]);
cout << ans << endl;
for (i=1;i<=n;i++)
{
if (!v[i]) cout << i << " -\n";
if (v[i+n]) cout << i << " +\n";
}
return 0;
}