[CODEVS 1301] 任务分配

描述

有N位工作人员,同时有N项任务, 每人必须承担一项任务,若给出某人不能从事的某些任务, 问要安排好工作,共有多少种方案?

http://codevs.cn/problem/1301/


分析

容斥原理的应用.

先看看样例:

四个人: A, B, C, D

A 不能选择: 2

B 不能选择: 2 3

C 不能选择: 3 4

D 不能选择: 4

总数是1~4全排列的个数 => 4! = 24

再考虑不能选的情况

那么

=>

采用 总数-非法个数 的方法计算, 而后者需用容斥原理计算.

answer :

= 4! - (|非法A + 非法B + 非法C + 非法D|)

= 4! - {|非法A| + |非法B| + |非法C| + |非法D| - |非法AB| - |非法AC| - |非法AD| - |非法BC| - |非法BD| - |非法CD| + |非法ABC| + |非法ABD| + |非法ACD| + |非法BCD| - |非法ABCD|}

= 4! - 3! - 2 * 3! - 2 * 3! - 3! + 2! + 2 * 2! + 2! + 3 * 2! + 2 * 2! + 2! - 1 - 1 - 1 - 1 + 0

= 4

容斥的实现

据说有三种实现容斥原理的方法 :

1. dfs

2. 队列数组

3. 二进制

只学了dfs法.

核心是统计各个阶乘的系数(coe), 记录在数组里, 最后高精统计.

根据 answer 的计算式子, 可以发现 : |P1 并 … Pm| m为奇数时, (n-m)! 的系数是负的. 容斥原理里这里是正的, 但别忘这里前头还有负号.

感觉这个dfs怪怪的… 先递归到底层, 又边回溯边更改.

变量表.

main() :

fac: 阶乘

cnt: 限制关系的个数

x[]: 人物

y[]: 任务

x[] <==> y[] // 一一对应

dfs() :

// 时间复杂度: O(2^15 = 32768)

coe[] 统计各个阶乘被计算了多少次

cur: 当前不匹配关系的编号

visx: 此人以考虑过

visy: 此任务已有人做

num: 当前正在统计 n-num 的阶乘的出现次数

|A1并A2并…并Anum|

num 为偶数 => coe[n-num]++

num 为奇数 => coe[n-num]–


代码

19ms 4MB

#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <sstream>
using namespace std;

const int maxn = 100 + 10;

struct bigint {
    int n, a[10000];
    static const int base = 10000;

    bigint operator += (const bigint& x) {
        n = max(n, x.n) + 1;
        for(int i = 0; i < n; i++) {
            a[i] += x.a[i];
            a[i + 1] += a[i] / base;
            a[i] %= base;
        }
        while(n > 0 && a[n - 1] == 0) n--;
        return *this;
    }

    bigint operator -= (const bigint& x) {
        for(int i = 0; i < n; i++) {
            if(a[i] < x.a[i]) {
                a[i] += base;
                a[i + 1] -= 1;
            }
            a[i] -= x.a[i];
        }
        while(n > 0 && a[n - 1] == 0) n--;
        return *this;
    }

    bigint operator * (const int& x) {
        bigint ans;
        ans.n = n + 1;
        memset(ans.a, 0, sizeof(ans.a));

        int rest = 0;
        for(int i = 0; i < ans.n; i++) {
            ans.a[i] = a[i] * x + rest;
            rest = ans.a[i] / base;
            ans.a[i] %= base;
        }
        while(ans.n > 0 && ans.a[ans.n - 1] == 0) ans.n--;
        return ans;
    }

    void print() {
        printf("%d", a[n - 1]);
        for(int i = n - 2; i >= 0; i--)
            printf("%04d", a[i]);
        printf("\n");
    }
};

int n, cnt, x[maxn], y[maxn], coe[maxn];
bool visx[maxn], visy[maxn];
bigint ans, fac[maxn];

// 当前正在考虑第 cur 对不匹配关系
// 正在计算 |A1 并 A2 并 ... 并 Anum|
void dfs(int cur, int num) {
    if(cur > cnt) coe[n - num] += (num & 1) ? -1 : 1;
    else {
        dfs(cur + 1, num);
        if(!visx[x[cur]] && !visy[y[cur]]) {
            visx[x[cur]] = visy[y[cur]] = 1;
            dfs(cur + 1, num + 1);
            visx[x[cur]] = visy[y[cur]] = 0;
        }
    }
}

int main() {
    cin >> n;
    fac[0] = (bigint) {1, {1}};
    for(int i = 1; i <= n; i++)
        fac[i] = fac[i - 1] * i;
    string tmp;
    getline(cin, tmp);
    for(int i = 0, j; i < n; i++) {
        string readline; // 不要定义在循环外, 因为如果没有读入readline, 会自动保留上次结果.
        getline(cin, readline);
        stringstream ss(readline);
        while(ss >> j) {
            cnt++;
            x[cnt] = i;
            y[cnt] = j;
        }
    }
    dfs(1, 0);

    // 统计
    for(int i = 0; i <= n; i++)
        if(coe[i] > 0) ans += fac[i] * coe[i];
    for(int i = 0; i <= n; i++)
        if(coe[i] < 0) ans -= fac[i] * (-coe[i]);
    ans.print();

    return 0;
}

主页

http://blog.csdn.net/qq_21110267

时间: 2024-10-17 22:52:20

[CODEVS 1301] 任务分配的相关文章

总结-数学

数学 比较害怕数学题, 因为数学题一般代码比较短, 一旦想到正解往往就能AC, 但是我数学水平很洼, 知道的东西也比较少. 感觉写写暴力拿部分分比较现实. 毕竟不是每个人都能找到正解. 1. 组合数 一般用阶乘计算, 需要求逆元. 可以用lucas定理优化时间复杂度. 组合类的问题就要考虑组合数 1. BestCoder-Round#33 第二题是组合数的题目 2. BZOJ-1005-明明的烦恼 用组合数阶乘公式推导出最后的式子 3. BZOJ-1951-古代猪文-SDOI2010-费马小定理

[ CodeVS冲杯之路 ] P2492

不充钱,你怎么AC? 题目:http://codevs.cn/problem/2492/ 在此先orz小胖子,教我怎么路径压缩链表,那么这样就可以在任意节点跳进链表啦(手动@LCF) 对于查询操作,直接树状数组(以下简称BIT)维护,修改操作就一个个暴力开方搞,再用差值单点更新BIT 不过这样会TLE,要加一点优化对不对,正如开头所说的路径压缩链表 路径压缩链表其实就是个并查集,在普通的链表里,删去两个连续的节点后会是下面这种情况,如删去2,3 当访问 2 的时候,会跳到3,但 3 已经删除了,

[CODEVS 1281] Xn数列

描述 给你6个数,m, a, c, x0, n, g Xn+1 = ( aXn + c ) mod m,求Xn http://codevs.cn/problem/1281/ 分析 比较裸的矩阵乘法题, 好久没做了, 写写思路 假设矩阵 A = { {a1, a2}, {a3, a4} }, B = { {b1, b2}, {b3, b4} }. 根据矩阵乘法的计算方法, 有 : A×B = { {a1b1+a2b2, a1b2+a2b4}, {a3b1+a4b3, a3b2+a4b4} }. 那

Codevs 1257 打砖块

1257 打砖块 http://codevs.cn/problem/1257/ 题目描述 Description 在一个凹槽中放置了n层砖块,最上面的一层有n块砖,第二层有n-1块,……最下面一层仅有一块砖.第i层的砖块从左至右编号为1,2,……i,第i层的第j块砖有一个价值a[i,j](a[i,j]<=50).下面是一个有5层砖块的例子.如果你要敲掉第i层的第j块砖的话,若i=1,你可以直接敲掉它,若i>1,则你必须先敲掉第i-1层的第j和第j+1块砖. 你的任务是从一个有n(n<=5

codevs——T1219 骑士游历

 http://codevs.cn/problem/1219/ 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 查看运行结果 题目描述 Description 设有一个n*m的棋盘(2≤n≤50,2≤m≤50),如下图,在棋盘上有一个中国象棋马. 规定: 1)马只能走日字 2)马只能向右跳 问给定起点x1,y1和终点x2,y2,求出马从x1,y1出发到x2,y2的合法路径条数. 输入描述 Input Description 第一行2个整数n和m 第二行4个

1683 车厢重组 codevs

http://codevs.cn/problem/1683/ 时间限制: 1 s 空间限制: 1000 KB 题目等级 : 白银 Silver 题目描述 Description 在一个旧式的火车站旁边有一座桥,其桥面可以绕河中心的桥墩水平旋转.一个车站的职工发现桥的长度最多能容纳两节车厢,如果将桥旋转180度,则可以把相邻两节车厢的位置交换,用这种方法可以重新排列车厢的顺序.于是他就负责用这座桥将进站的车厢按车厢号从小到大排列.他退休后,火车站决定将这一工作自动化,其中一项重要的工作是编一个程序

codevs 1487 大批整数排序(水题日常)

时间限制: 3 s 空间限制: 16000 KB 题目等级 : 黄金 Gold 题目描述 Description !!!CodeVS开发者有话说: codevs自从换了评测机,新评测机的内存计算机制发生变化 计算内存的时候会包括栈空间 swap空间 这题的2M是单指内存空间... 十分十分抱歉 抱歉 !!! 现在有一大批(总数不超过10000000个)1到10之间的整数,现在请你从小到大进行排序输出. (测试数据将超过11MB.) 输入描述 Input Description 第一行表示将下排序

codevs 3342 绿色通道 (二分+线性DP)

codevs 3342 绿色通道 http://codevs.cn/problem/3342/ 难度等级:黄金 题目描述 Description <思远高考绿色通道>(Green Passage, GP)是唐山一中常用的练习册之一,其题量之大深受lsz等许多oiers的痛恨,其中又以数学绿色通道为最.2007年某月某日,soon-if (数学课代表),又一次宣布收这本作业,而lsz还一点也没有写…… 高二数学<绿色通道>总共有n道题目要写(其实是抄),编号1..n,抄每道题所花时间

[ CodeVS冲杯之路 ] P1368

不充钱,你怎么AC? 题目:http://codevs.cn/problem/1368/ 嗯……泡泡堂,很劲啊,其实就是个盗版的田忌赛马 http://www.cnblogs.com/hyfer/p/5853381.html 这边博客讲得很好啊,虽然不是这道题,但是方法是完全类似的 你把田忌赛马看懂了,泡泡堂就自然解出来了 1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<c