• 保镖


    Description

    蒟蒻 (YxuanwKeith) 想成为 (Philisweng) 的保镖,但是作为预备队员的保镖智商肯定也不能低,至少要回答出下面这个问题:现在有一副若干条边的二分图,左边有 (N) 个点 (a_i) ,右边有 (M) 个点 (b_i) ,每个点都有一个权值 (w_i) 。一个合法的子图满足以下两个限制:

    1. 选出的点权和大于等于限制 (t)

    2. 并且可以从图中选出若干条边, 使得二分图中每个点最多被一条边覆盖,而选出的点要恰好被一条边覆盖。

    求总方案数。由于 (YxuanwKeith) 很弱,所以他找到你来回答这个问题。

    Input

    第一行包括两个数 (N,M) ,分别表示左边点的个数和右边点的个数。

    (2) 行到第 (N+1) 行,第 (i) 行一个长度为 (M) 的字符串 (S_i) ,第 (j) 个字符如果是 (0) 表示左边第 (i-1) 个点和右边第 (j) 个点没有连边,如果是 (1) 表示有连边。

    (N+2) 行, (N) 个非负整数,第 (i) 个非负整数表示左边第 (i) 个点的权值 (W_i)

    (N+3) 行, (M) 个非负整数,第 (i) 个非负整数表示右边第 (i) 个点的权值 (v_i)

    (N+4) 行,一个正整数 (t) ,表示题目中的限制。

    Output

    输出共一行,一个数,表示答案。

    Sample

    Sample Input

    3 3
    010
    111
    010
    1 2 3
    8 5 13
    21
    

    Sample Output

    3
    

    Limit

    对于 (30\%) 的数据: (N,Mle 8)

    对于另外 (30\%) 的数据:左边的点和右边的点两两间都有连边。

    对于 (100\%) 的数据: (N,Mle 20)(tle 4 imes 10^8)(w_i, v_ile 10^8)

    Solution

    首先讲一下 (Hall) 定理:

    • 对于一个二分图具备完备匹配的充要条件是对于左边点的每一个子集,和子集内的点相连的点集大小大于等于子集大小。

    用状压的方式找出两边都合法的方案,得到两个数组,用双指针在这两个数组上搞一搞就可以统计答案了。

    具体见代码,自认为还是很清晰易懂的。

    #include<bits/stdc++.h>
    using namespace std;
    #define N 21
    #define rep(i, a, b) for (int i = a; i <= b; i++)
    #define ll long long
    int n, m, _n, g[2][1 << N], f[2][1 << N], w[N << 2], rec[2][1 << N], t; ll ans;
    char s[N];
    void solve(int n, int t) {
    	rep(S, 0, (1 << n) - 1) {
    		f[t][S] = 1; int SS = 0; ll sum = 0;
    		rep(i, 1, n) if ((S >> (i - 1)) & 1) {
    			int _S = S ^ (1 << i - 1); if (!f[t][_S]) { f[t][S] = 0; break; }
    			SS |= g[t][i], sum += w[t * _n + i];
    		}
    		if (__builtin_popcount(SS) < __builtin_popcount(S)) f[t][S] = 0;
    		if (f[t][S]) rec[t][++rec[t][0]] = sum;
    	}
    }
    int main() {
    	freopen("guard.in", "r", stdin); freopen("guard.out", "w", stdout);
    	scanf("%d%d", &n, &m); _n = n;
    	rep(i, 1, n) {
    		scanf("%s", s + 1);
    		rep(j, 1, m)  if (s[j] == '1') g[0][i] |= 1 << (j - 1), g[1][j] |= 1 << (i - 1);
    	}
    	rep(i, 1, n + m) scanf("%d", &w[i]); scanf("%d", &t);
    	solve(n, 0), solve(m, 1);
    	rep(x, 0, 1) sort(rec[x] + 1, rec[x] + rec[x][0] + 1);
    	int q = rec[1][0] + 1;
    	rep(p, 1, rec[0][0]) {
    		while (q > 1 && rec[1][q - 1] + rec[0][p] >= t) q--;
    		if (rec[1][q] + rec[0][p] >= t) ans += rec[1][0] - q + 1;
    	}
    	printf("%lld", ans);
    	return 0;
    }
    
  • 相关阅读:
    python 获取项目的根路径
    信息系统项目管理师-1.4软件工程
    信息系统项目管理师
    信息系统项目管理师-导学
    信息系统项目管理师-考试安排
    读取text行列
    pipeline基本框架思维
    Linux压缩隐藏文件
    python+appium环境搭建
    leetcode for mysql
  • 原文地址:https://www.cnblogs.com/aziint/p/8463484.html
Copyright © 2020-2023  润新知