题目大意

 

  给出一个或与表达式,每个正变量和反变量最多出现一次,询问是否存在一种方案使得每个或式中有且仅有一个变量的值为1。

解题分析

  将每个变量拆成三个点x,y,z。 y表示对应的正变量,z表示对应的反变量。

  由S向每个点的x部连一条流量为1的边,表示该变量的某个正变量或反变量的取值为1。

  由每个点的x部向y部和z部分别连一条流量为1的边,表示每个正变量和反变量仅有一个取值为1。

  若某个或式中含有某个变量,则由该变量的y部或z部向或式连一条流量为1的边。表示该变量可以使该或式的结果为1。

  由每个或式向T连一条流量为1的边,表示该或式被满足,且仅有一个变量的取值为1。

  仅仅这么连还是不够的,因为如果某个变量的正变量和反变量同时出现,那么正变量或反变量中必定有一个值为1,对应到图上则是S向该变量的x部必须有1的流量。

  所以可以向这条边添加上-1的费用,使得该变量的值优先被满足。

  跑一遍最小费用最大流,如果是满流且费用的绝对值等于必须被确定值的变量的数量,则说明可以成功满足。

  输出方案时只需遍历一下残余网络中的边,如果残余网络中没有流量则说明该变量被选择了,最后注意一下细节,确定一下对于每个变量选1还是选0。

参考程序

 #include <bits/stdc++.h>
using namespace std; #define rep(i,x,y) for (int i=x;i<=y;i++)
const int N=;
const int INF=; int n,m,S,T,sum,minC,maxF;
int lt[N*],flag[N],dis[N*],pd[N*],pre[N*],sgn[N*];
vector <int> vec[N]; struct edge{
int u,v,f,w,nt;
}eg[N*]; void add(int u,int v,int f,int w)
{
//cout<<u<<" "<<v<<" "<<f<<" "<<w<<endl;
eg[++sum]=(edge){u,v,f,w,lt[u]}; lt[u]=sum;
eg[++sum]=(edge){v,u,,-w,lt[v]}; lt[v]=sum;
} bool spfa()
{
queue <int> Q;
rep (i,S,T) dis[i]=INF,pd[i]=,pre[i]=-;
dis[S]=; pd[S]=; Q.push(S);
while (!Q.empty())
{
int u=Q.front();
for (int i=lt[u];i;i=eg[i].nt)
{
int v=eg[i].v;
if (eg[i].f>)
{
if (dis[u]+eg[i].w<dis[v])
{
dis[v]=dis[u]+eg[i].w;
pre[v]=i;
if (!pd[v])
{
Q.push(v);
pd[v]=;
}
}
}
}
pd[u]=; Q.pop();
}
return dis[T]!=INF;
}
void minCmaxF()
{
int flow;
while (spfa())
{
flow=INF;
for (int i=pre[T];~i;i=pre[eg[i].u])
flow=min(flow,eg[i].f);
for (int i=pre[T];~i;i=pre[eg[i].u])
eg[i].f-=flow,eg[i^].f+=flow;
maxF+=flow;
minC+=flow*dis[T];
}
} int main()
{
freopen("sat.in","r",stdin);
freopen("sat.out","w",stdout); cin.sync_with_stdio();
sum=; memset(lt,,sizeof(lt));
int limit=; cin>>n>>m;
S=; T=*n+m+;
rep(i,,m)
{
int num; cin>>num;
rep(j,,num)
{
int x; cin>>x;
flag[abs(x)]++;
if (x>) sgn[x]=; else sgn[x]=-;
vec[i].push_back(x);
}
}
rep(i,,n) if (flag[i]==) add(S,i,,-),limit++; else add(S,i,,);
rep(i,,n) {add(i,n+*i-,,); add(i,n+*i,,);}
rep(i,,m)
{
for (auto v:vec[i])
{
int x=v>?n+*abs(v)-:n+*abs(v);
add(x,n*+i,,);
}
add(*n+i,T,,);
}
minC=maxF=;
minCmaxF();
vector <int> ans;
if (minC==-limit && maxF==m)
{
cout<<"YES"<<endl;
for (int i=lt[S];i;i=eg[i].nt)
{
if (eg[i].f==)
{
if (sgn[eg[i].v]==) ans.push_back(); else ans.push_back();
}
else
{
if (eg[lt[eg[i].v]].f==) ans.push_back(); else ans.push_back();
}
}
for (int i=ans.size()-;i>=;i--) cout<<ans[i]<<" ";
cout<<endl;
}
else cout<<"NO"<<endl;
}

codeforces gym 100357 I (费用流)的更多相关文章

  1. CodeForces 164C Machine Programming 费用流

    Machine Programming 题目连接: http://codeforces.com/problemset/problem/164/B Descriptionww.co One remark ...

  2. Codeforces 708D 上下界费用流

    给你一个网络流的图 图中可能会有流量不平衡和流量>容量的情况存在 每调整一单位的流量/容量 需要一个单位的花费 问最少需要多少花费使得原图调整为正确(可行)的网络流 设当前边信息为(u,v,f, ...

  3. CodeForces 1187G Gang Up 费用流

    题解: 先按时间轴将一个点拆成100个点. 第一个点相当于第一秒, 第二个点相当于第二秒. 在这些点之间连边, 每1流量的费用为c. 再将图上的边也拆开. 将 u_i 向 v_i+1 建边. 将 v_ ...

  4. codeforces gym 100357 J (网络流)

    题目大意 有n种物品,m种建筑,p个人. n,m,p∈[1,20] 每种建筑需要若干个若干种物品来建造.每个人打算建造一种建筑,拥有一些物品. 主角需要通过交易来建造自己的建筑,交易的前提是对方用多余 ...

  5. codeforces gym 100357 H (DP 高精度)

    题目大意 有r*s张扑克牌,数字从1到 r,每种数字有s种颜色. 询问对于所有随机的d张牌,能选出c张组成顺子的概率和组成同花的概率. 解题分析 对于组成顺子的概率,令dp[i][j][k]表示一共选 ...

  6. codeforces gym 100357 K (表达式 模拟)

    题目大意 将一个含有+,-,^,()的表达式按照运算顺序转换成树状的形式. 解题分析 用递归的方式来处理表达式,首先直接去掉两边的括号(如果不止一对全部去光),然后找出不在括号内且优先级最低的符号.如 ...

  7. Gym - 101492I 区间限制费用流

    https://cn.vjudge.net/problem/Gym-101492I 如果用单个点代表每个区间 利用拆点来限制区间的流量的话 点是 n^2/2+m个 边是2*n^2条 但是这样会T 解法 ...

  8. Codeforces Gym 100002 E "Evacuation Plan" 费用流

    "Evacuation Plan" Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/10 ...

  9. Codeforces Gym 101190M Mole Tunnels - 费用流

    题目传送门 传送门 题目大意 $m$只鼹鼠有$n$个巢穴,$n - 1$条长度为$1$的通道将它们连通且第$i(i > 1)$个巢穴与第$\left\lfloor \frac{i}{2}\rig ...

随机推荐

  1. 思维+multiset ZOJ Monthly, July 2015 - H Twelves Monkeys

    题目传送门 /* 题意:n个时刻点,m次时光穿梭,告诉的起点和终点,q次询问,每次询问t时刻t之前有多少时刻点是可以通过两种不同的路径到达 思维:对于当前p时间,从现在到未来穿越到过去的是有效的值,排 ...

  2. 枚举+贪心 HDOJ 4932 Miaomiao's Geometry

    题目传送门 /* 题意:有n个点,用相同的线段去覆盖,当点在线段的端点才行,还有线段之间不相交 枚举+贪心:有坑点是两个点在同时一条线段的两个端点上,枚举两点之间的距离或者距离一半,尽量往左边放,否则 ...

  3. ACM_数数有多少(第二类Stirling数-递推dp)

    数数有多少 Time Limit: 2000/1000ms (Java/Others) Problem Description: 小财最近新开了一家公司,招了n个员工,但是因为资金问题,办公楼只有m间 ...

  4. log4net 简易封装

    using log4net; using log4net.Appender; using log4net.Config; using log4net.Core; using log4net.Layou ...

  5. sql server 行转列 要注意的问题 pivot

      select * from (  select mvqr.VoteQuestionId,mvqr.AnswerSolution from  JY_MemberVoteQuestionRef as  ...

  6. FormsAuthentication权限管理

    通常我们在做访问权限管理的时候会把用户正确登录后的基本信息保存在Session中然后用户每次请求页面或接口数据的时候代上会话状态即能拿到Session中存储的基本信息Session的原理,也就是在服务 ...

  7. poi获取word批注

    package test; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundExcept ...

  8. DVWA--登录页面错误问题 469 | | PHP Fatal error: Uncaught exception 'PDOException' with message 'could not find driver' in C:\web\DVWA\dvwa\includes\dvwaPage.inc.php:469

    // MySQL PDO Prepared Statements (for impossible levels) $db = new PDO('mysql:host=' . $_DVWA[ 'db_s ...

  9. crontab定时清理日志

    1.创建shell脚本 vi test_cron.sh #!/bin/bash#echo "====`date`====" >> /game/webapp/test_c ...

  10. [转载] kprobe原理解析(一)

    From: https://www.cnblogs.com/honpey/p/4575928.html kprobe原理解析(一) kprobe是linux内核的一个重要特性,是一个轻量级的内核调试工 ...