保镖(hall定理&&集合动规&&优化)
来源:互联网 发布:秦风拍牌软件 编辑:程序博客网 时间:2024/05/01 09:20
【问题描述】
蒟蒻YxuanwKeith想成为Philisweng的保镖,但是作为预备队员的保镖智商肯定也不能低,至少要回答出下面这个问题:现在有一副若干条边的二分图,左边有N个点ai,右边有M个点bi,每个点都有一个权值wi。一个合法的子图满足以下两个限制:
1.选出的点权和大于等于限制t。
2.并且可以从图中选出若干条边,使得二分图中每个点最多被一条边覆盖,而选出的点要恰好被一条边覆盖。
求总方案数。由于YxuanwKeith很弱,所以他找到你来回答这个问题。
【输入格式】
第一行包括两个数N,M,分别表示左边点的个数和右边点的个数。
第2行到第N+1行,第i行一个长度为M的字符串Si,第j个字符如果是0表示左边第i-1个点和右边第j个点没有连边,如果是1表示有连边。
第N+2行,N个非负整数,第i个非负整数表示左边第i个点的权值Wi
第N+3行,M个非负整数,第i个非负整数表示右边第i个点的权值vi
第N+4行,一个正整数t,表示题目中的限制。
【输出格式】
输出共一行,一个数,表示答案。
【输入样例】
3 3
010
111
010
1 2 3
8 5 13
21
【输出样例】
3
【样例解释】
样例中的二分图如图所示:
子集{a1,a2,b2,b3}可以通过选择边集{(a1,b2),(a2,b3)}满足条件二,并且权值和是21。
子集{a3,b2,b3}和{a2,a3,b2,b3}都可以通过选择边集{(a2,b2),(a3,b2)}满足两个条件。
其余方案都不合法,所以答案是3。
【数据范围】
对于30%的数据:N,M<=8
对于另外30%的数据:左边的点和右边的点两两间都有连边。
对于100%的数据:N,M<=20,t<=4*10^8,wi,vi<=10^8
【来源】
https://jzoj.net
一道很神奇的题,开始用的网络流,结果全错。
正解是集合动规,主要是要用hall定理(请自行百度)
根据hall定理我们可以用动规的思想来查看一个集合是否成立,然后把权值和记录下来,这样我们就可以得到2个数组。(不成立的权值应该是负无穷)
然后一个排序,在另一个上进行二分查找就可以了。
一些必要的优化请自行查看代码
完整代码如下:
#include<cstdlib>#include<cstdio>#include<cstring>#include<iostream>#include<vector>#include<algorithm>using namespace std;typedef long long ll;const int maxn=45;const ll inf=20000000000000ll;vector<int>g[maxn];ll d[1<<21]={0},q[1<<21]={0};int m,n,a[maxn];bool vis[21];ll b[maxn];ll t;char s[maxn];ll work(int x){ int num1=0,num2=0; memset(vis,0,sizeof(vis)); for(int i=1;i<=n;i++) if((a[i]&x)==a[i]) { num1++; for(int k=0;k<g[i].size();k++) if(!vis[g[i][k]]) vis[g[i][k]]=1,num2++; if(num2>=n-i+num1) return 1; } return num1<=num2;}ll work2(int x){ int num1=0,num2=0; memset(vis,0,sizeof(vis)); for(int i=1;i<=m;i++) if((a[i]&x)==a[i]) { num1++; for(int k=0;k<g[i+n].size();k++) if(!vis[g[i+n][k]]) vis[g[i+n][k]]=1,num2++; if(num2>=m-i+num1) return 1; } return num1<=num2;}int main(){ //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%s",s); for(int k=0;k<m;k++) if(s[k]=='1') { g[i].push_back(k+1); g[k+1+n].push_back(i); } } for(int i=1;i<=n+m;i++) cin>>b[i]; cin>>t; a[1]=1; for(int i=2;i<=max(n,m);i++)a[i]=a[i-1]*2; int k1=1<<n,k2=1<<m; for(int i=1;i<k1;i++) { if(d[i]!=-inf&&!work(i)) //根据hall定理进行判断 for(int j=i;j<k1;j++) if((i&j)==i) d[j]=-inf; } for(int i=1;i<k2;i++) { if(q[i]!=-inf&&!work2(i)) for(int j=i;j<k2;j++) if((i&j)==i) q[j]=-inf; } for(int i=1;i<k1;i++) if(d[i]!=-inf) for(int j=1;j<=n;j++) if((a[j]&i)==a[j])//算权值和的时候的优化 { d[i]=b[j]+d[i-a[j]]; break; } for(int i=1;i<k2;i++) if(q[i]!=-inf) for(int j=1;j<=m;j++) if((a[j]&i)==a[j]) { q[i]=b[j+n]+q[i-a[j]]; break; } sort(d,d+k1); ll ans=0; for(int i=0;i<k2;i++) if(q[i]!=-inf)//二分找答案 { ans+=k1-(lower_bound(d,d+k1,t-q[i])-d); } cout<<ans; return 0;}
- 保镖(hall定理&&集合动规&&优化)
- NOI模拟:保镖(Hall定理)
- jzoj 5000. 【NOI2017模拟3.4】保镖 hall定理+搜索
- BZOJ3693:圆桌会议(Hall定理)
- HALL定理
- Hall定理(bzoj 1135: [POI2009]Lyz)
- Hall定理学习小记
- LOJ 6062 [Hall定理]
- Hall定理(二分图匹配问题,Hungary算法基础)
- Trie树(集合动规)
- 保镖
- Educational Codeforces Round 8 F. Bear and Fair Set(最大流 | Hall定理)
- Educational Codeforces Round 8 F. Bear and Fair Set(最大流 | Hall定理)★
- BZOJ 2138: stone Hall定理 线段树
- loj 6062 (hall定理+ 线段树)
- K排列问题[2] (集合动规)
- 【BZOJ】【P1135】【POI2009】【Lyz】【题解】【线段树+Hall定理】
- 【hall定理】一个关于二分图的问题
- python——配置环境变量
- string类
- 在代码段中使用数据(0601)
- Opencv 和 c++ 框出图片中的目标
- leetcode 504. Base 7 python
- 保镖(hall定理&&集合动规&&优化)
- MyBatis缓存
- PAT (Advanced Level) 1004. Counting Leaves (30)
- 受限制玻尔兹曼机RBM原理简介
- 斐波那契字符串
- 4.从头到尾打印链表
- 使用R语言的dplyr包进行数据预处理
- opencv ml模块 SVM使用笔记
- jquery触发键盘按下事件