隐藏页面特效

1301 任务分配

1301 任务分配

 

2003年浙江省队选拔赛

 时间限制: 1 s
 空间限制: 128000 KB
 题目等级 : 大师 Master
 
 
题目描述 Description

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

输入描述 Input Description

输入文件第1行为N(1<=N<=100), 以下N行,其中第i+1行表示第i个人不能从事的任务编号, 任务之间用空隔分开, 若第i个人没有限制条件,则第i+1行为空行, 所有人员不能从事的任务之和不大于25。

输出描述 Output Description

输出文件只有1行,为所有满足条件的分配方案数。

样例输入 Sample Input

  4

  2

  2 3

  3 4

  4

样例输出 Sample Output

4

数据范围及提示 Data Size & Hint

如题

分类标签 Tags 点此展开 

 

分析

容斥原理的应用.

先看看样例:
四个人: 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]–-

 

来自:http://blog.csdn.net/qq_21110267/article/details/43882591

 

AC代码:

#include <cstdio> #include <string> #include <cstring> #include <iostream> #include <sstream> using namespace std; const int maxn=100+10; struct bigint{ static const int base=10000; int n,a[base]; 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; return ; } 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; }

 


__EOF__

本文作者shenben
本文链接https://www.cnblogs.com/shenben/p/6053384.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   神犇(shenben)  阅读(192)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
点击右上角即可分享
微信分享提示