POJ 3801/HDU 3157 Crazy Circuits | 有下界的最小流

时间:2022-02-03 18:19:37

题目:

POJ最近总是炸

所以还是用HDU吧http://acm.hdu.edu.cn/showproblem.php?pid=3157


题解:

题很长,但其实就是给个有源汇带下界网络流(+是源,-是汇),求最小流

求法:

1.模仿可行流建图,但是不加t到s的INF边

2.跑最大流

3.加t到sINF边

4.跑最大流

5.如果两次答案相加不等于sum,无解;

6.如果有解,t到s的反边流量就是答案

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define N 205
#define M 20005
#define INF 0x3f3f3f3f
using namespace std;
int n,m,s,t,S,T,c,cur[N],head[N],d[N],sum,ecnt=,ans,lev[N];
char a[N],b[N];
queue<int> q;
struct adj
{
int nxt,v,w;
}e[M];
int calc(char j[])
{
if (j[]=='+') return n+;
if (j[]=='-') return n+;
int ans=,s=strlen(j);
for (int i=;i<s;i++) ans*=,ans+=j[i]-'';
return ans;
}
void add(int u,int v,int w)
{
e[++ecnt].v=v;e[ecnt].w=w;e[ecnt].nxt=head[u];head[u]=ecnt;
e[++ecnt].v=u;e[ecnt].w=;e[ecnt].nxt=head[v];head[v]=ecnt;
}
void init()
{
memset(head,,sizeof(head));
memset(d,,sizeof(d));
ecnt=;ans=sum=;
}
bool bfs()
{
for (int i=;i<=T;i++)
cur[i]=head[i],lev[i]=-;
q.push(S);lev[S]=;
while (!q.empty())
{
int u=q.front();q.pop();
for (int i=head[u],v;i;i=e[i].nxt)
if (lev[v=e[i].v]==- && e[i].w>)
q.push(v),lev[v]=lev[u]+;
}
return lev[T]!=-;
}
int dfs(int u,int flow)
{
if (u==T) return flow;
int ret=,v,delta;
for (int &i=cur[u];i;i=e[i].nxt)
if (lev[v=e[i].v]==lev[u]+ && e[i].w>)
{
delta=dfs(v,min(flow-ret,e[i].w));
if (delta)
{
e[i].w-=delta;e[i^].w+=delta;ret+=delta;
if (ret==flow) break;
}
}
return ret;
}
int main()
{
while (scanf("%d%d",&n,&m)!=EOF)
{
if (n== && m==) break;
init();
s=n+;t=s+;S=t+;T=S+;
for (int i=,u,v;i<=m;i++)
{
scanf("%s%s%d",a,b,&c);
u=calc(a);v=calc(b);
add(u,v,INF-c);
d[u]-=c;d[v]+=c;
}
for (int i=;i<=n+;i++)
if (d[i]>) add(S,i,d[i]),sum+=d[i];
else if (d[i]<) add(i,T,-d[i]);
while (bfs()) ans+=dfs(S,INF);
add(t,s,INF);
while (bfs()) ans+=dfs(S,INF);
if (ans!=sum) puts("impossible");
else printf("%d\n",e[ecnt].w);
}
return ;
}