【问题描述】
呵呵,欧教又出了一套题,准备用来考察13 级众基友的OI 水平:“代码都是NOIP 难度,多琢磨一下。”
虽然明知要被坑,但是众基友还是决定齐心合力来解决这套坑爹题。
这套题一共有n 道,而停课的基友却只有8 个。
对于一道题,它会完全占用某基友[a,b]区间的时间。当然,不是所有的基友会做这些题,比如小兽做BFS 不加边框,小蛋基本不会做题……
作为基房的女王,神母牛小希想要知道是否存在一种方案使得众基友能否完成这套题——若存在则回答“yes”,反之则回答“hehe”。
【输入数据】
首先一个整数sum,表示一共有sum 组测试数据。
对于每一组数据:
第一行一个整数n,表示一共有n 道题。
接下来n 行每行若干个整数形如a,b,m,h1,h2,…,hm。第i 行表示工作i 会占用[a,b]的时间且只有m 个基友会做,他们是h1,h2,…,hm。
【输出数据】
一共sum 行,每行为“yes”或“hehe”。
【样例输入】
2
2
2 2 2 1 3
2 2 2 1 3
2
2 2 2 1 3
2 2 2 1 3
【样例输出】
yes
yes
【数据范围及约定】
1≤sum≤10,0≤m≤8,1≤n≤12。
所有输入数据均在longint 范围内。
此题考察状态压缩的应用。普通的状压显然行不通,因为有一个矛盾不能解决——一个人在不同时间段可能完成不同的任务。
那么一种思想就是将人和任务的关系倒过来,让人去选任务,而不是让任务去选人。
Accode:
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <string>
const int maxN = 20;
struct Seg {int L, R, sta;} seg[maxN];
bool f[2][1 << 13];
int L[maxN], R[maxN], can[maxN];
int cnt[2], status[2][1 << 13];
int n, m, pst, ths;
inline int getint()
{
int res = 0; char tmp;
while (!isdigit(tmp = getchar()));
do res = (res << 3) + (res << 1) + tmp - '0';
while (isdigit(tmp = getchar()));
return res;
}
void Dfs(int can, int S, int i, int R)
//can为这个人能做的任务,S为已经做过的任务,
//i为待枚举的任务,R为上一个任务的右界(保证不能冲突)。
{
if (i >= m)
{
if (!f[ths][S])
f[ths][status[ths][cnt[ths]++] = S] = 1;
return;
}
Dfs(can, S, i + 1, R);
//这里一定不能把之前的人做的任务的时间
//冲突也一并算进去,否则后果很严重。
if ((can & (1 << i)) &&
!(S & (1 << i))
&& seg[i].L > R)
Dfs(can, S | (1 << i), i + 1, seg[i].R);
//零壹枚举所有无冲突的任务。
return;
}
int cmp(const void *a, const void *b)
{
if (((Seg *)a) -> R - ((Seg *)b) -> R)
return ((Seg *)a) -> R - ((Seg *)b) -> R;
return ((Seg *)a) -> L - ((Seg *)b) -> L;
}
//按照区间的右界排序(左界无所谓),
//方便后面判断冲突。
int main()
{
freopen("problem.in", "r", stdin);
freopen("problem.out", "w", stdout);
for (int T = getint(); T; --T)
{
memset(f, 0, sizeof f);
memset(can, 0, sizeof can);
//多组数据注意清零。
m = getint(); n = 0;
for (int i = 0; i < m; ++i)
{
seg[i].L = getint();
seg[i].R = getint();
seg[i].sta = 0;
for (int cnt = getint(); cnt; --cnt)
{
int tmp = getint() - 1;
seg[i].sta |= 1 << tmp;
n = std::max(m, tmp + 1);
}
}
qsort(seg, m, sizeof seg[0], cmp);
for (int i = 0; i < m; ++i)
for (int j = 0; j < n; ++j)
if (seg[i].sta & (1 << j))
can[j] |= 1 << i;
pst = 0, ths = 1;
status[ths][(cnt[ths] = 0)++] = 0;
f[ths][0] = 1;
for (int i = 0; i < n; ++i)
{
std::swap(pst, ths); cnt[ths] = 0;
memset(f[ths], 0, sizeof f[ths]);
//此数组一定要清零,相当于判断状态是否相同的Hash表。
for (int k = 0; k < cnt[pst]; ++k)
Dfs(can[i], status[pst][k], 0, 0);
if (f[ths][(1 << m) - 1]) break;
}
printf("%s\n", f[ths][(1 << m) - 1]
? "yes" : "hehe");
}
return 0;
}