toj1050Courses

来源:互联网 发布:迅雷赚钱宝 监控软件 编辑:程序博客网 时间:2024/06/11 04:37
1050.   Courses
Time Limit: 5.0 Seconds   Memory Limit:65536K
Total Runs: 1339   Accepted Runs:623



Consider a group of N students and P courses. Each student visits zero, one or more than one courses. Your task is to determine whether it is possible to form a committee of exactly P students that satisfies simultaneously the conditions:

. every student in the committee represents a different course (a student can represent a course if he/she visits that course)

. each course has a representative in the committee

Your program should read sets of data from a text file. The first line of the input file contains the number of the data sets. Each data set is presented in the following format:

P N
Count1 Student1 1 Student1 2 ... Student1 Count1
Count2 Student2 1 Student2 2 ... Student2 Count2
......
CountP StudentP 1 StudentP 2 ... StudentP CountP

The first line in each data set contains two positive integers separated by one blank: P (1 ≤ P ≤ 100) - the number of courses and N (1 ≤ N ≤ 300) - the number of students. The next P lines describe in sequence of the courses . from course 1 to course P, each line describing a course. The description of course i is a line that starts with an integer Count i (0 ≤ Count i ≤ N) representing the number of students visiting course i. Next, after a blank, you'll find the Count i students, visiting the course, each two consecutive separated by one blank. Students are numbered with the positive integers from 1 to N.

There are no blank lines between consecutive sets of data. Input data are correct.

The result of the program is on the standard output. For each input data set the program prints on a single line "YES" if it is possible to form a committee and "NO" otherwise. There should not be any leading blanks at the start of the line.

Sample Input

23 33 1 2 32 1 21 13 32 1 32 1 31 1

Sample Output

YESNO


Source: Southeastern European 2000

呃,这个这个题,它其实更应该是一个二分匹配,但是呐,还木有认真学习,只是知道有这么一个概念,这两天搞最大流,看到这个题,就有种感觉...不过可以肯定的是,这题用网络流做

效率并不好.凑合一下.以后学了其它姿势再说~

当然,他给了5秒时间,足够了.不过很可惜的是,刚开始用邻接矩阵建图,果果地超时了.俺就想,这题可能会卡,为啥?

邻接矩阵建图和邻接链表建图最大的差异就在于前者每次寻找后继节点时需要遍历所有节点,这让很多童鞋误会它效率低下.

其实不然,主要还是看用在甚么地方.我个人的一点体会是: 在节点数不多(500以内吧),并且可能会出现很多很多重边的情况下,邻接矩阵的优势是很明显的,因为它极好

地合并了重边,比如在建s->t流量cap的边时G[s][t] += cap, 如果有重边s->t流量cap2,直接就是G[s][t] += cap2.相当于直接合并了.

而邻接链表处理时就不太妙,如果要实现合并重边,在合并时就需要遍历地查找.当然可以加入数组优化,使查找代价降到O(1),不过实现起来还是比较麻烦的.

所以我的看法是,根据实际数据判断,估算.一般就没有问题了.昨天陈老湿就遇到了用链表TE,用矩阵可以搞的题目...

比如这题把,课程和人都需要拆点,那么点数至少是:300*2+100*2,加两个超级点,边数会非常大,但是重边呢?几乎不该有重边,对吧?这直接导致了用矩阵时开销很大,最后

超时的结局....所以我马上改用链表,就可以A了,当然有了超时的教训,我在链表实现时顺便也加上了当前弧优化,可以略微提升效率.

Time: 1.59 sec.    Memory: 2032 K

#include <cstdio>#include <cstring>#include <algorithm>#include <queue>#include <vector>#include <climits>using namespace std;const int INF = INT_MAX>>1;const int MAX_V = 1024;struct edge {int to, cap, rev; };vector<edge> G[MAX_V];int level[MAX_V];int iter[MAX_V]; //当前弧int P, N, all;inline void add(int s, int t, int cap) {G[s].push_back((edge){t,cap, G[t].size()});G[t].push_back((edge){s,0,G[s].size()-1});}bool bfs(int s, int t) {memset(level, -1, sizeof(level));queue<int> Q;int p;Q.push(s);level[s] = 0;while (!Q.empty()) {p = Q.front();Q.pop();for (int i = 0; i < G[p].size(); ++i) {edge &e = G[p][i];if (e.cap > 0 && level[e.to] < 0) {level[e.to] = level[p] + 1;if (e.to == t) return true;Q.push(e.to);}}}return false;}int dfs(int s, int t, int flow) {if (s == t) return flow;int d, res = 0;for (int &i = iter[s]; i < G[s].size(); ++i) {edge &e = G[s][i];if (e.cap > 0 && level[e.to] == level[s] + 1) {d = dfs(e.to, t, min(flow, e.cap));res += d, e.cap -= d, flow -= d;G[e.to][e.rev].cap += d;}}return res;}int dinic(int s, int t) {int flow = 0;while (bfs(s,t)) {memset(iter, 0, sizeof(iter));flow += dfs(s, t, INF);}return flow;}int main() {int T;scanf(" %d", &T);while (T--) {scanf(" %d %d ", &P, &N); //P门课,N个人for (int i = 0; i < MAX_V; ++i) G[i].clear(); //initlize/* 0超级源点 * 1~P课左 * P+1~2P课右 * 2P+1~2P+N人左  * 2P+N+1~2P+2N人右 * 2P+2N+1超级汇点,记为all */all = ((N+P)<<1)|1;int cap, m;for (int i = 1; i <= P; ++i) {scanf(" %d", &cap);add(0, i, cap);add(i, P+i, 1);for (int j = 1; j <= cap; ++j) {scanf(" %d", &m);add(P+i, (P<<1)+m, 1);}}for (int i = 1; i <= N; ++i) {add((P<<1)+i, (P<<1)+N+i, 1);add((P<<1)+N+i, all, 1);}puts(dinic(0, all) == P ? "YES" : "NO");}return 0;}#ifdef DEBUGint G[MAX_V][MAX_V];int level[MAX_V];int N, P, all;bool bfs(int s, int t) {memset(level, -1, sizeof(level));queue<int> Q;int p;level[s] = 0;Q.push(s);while (!Q.empty()) {p = Q.front();Q.pop();for (int i = 0; i <= all; ++i) {if (G[p][i] > 0 && level[i] < 0) {level[i] = level[p] + 1;if (i == t) return true;Q.push(i);}}}return false;}int dfs(int s, int t, int flow) {if (s == t) return flow;int res = 0, tmp;for (int i = 0; i <= all; ++i) {if (G[s][i] > 0 && level[i] == level[s] + 1) {tmp = dfs(i, t, min(flow, G[s][i]));res += tmp, G[s][i] -= tmp, G[i][s] += tmp;flow -= tmp;}}return res;}int dinic(int s, int t) {int flow = 0;while (bfs(s, t)) flow += dfs(s, t, INF);return flow;}int main() {int T;scanf(" %d", &T);while (T--) {scanf(" %d %d", &P, &N); //P门课,N个人all = ((N+P)<<1)|1;int s, t;memset(G, 0, sizeof(G));/* 源点->课左->课右->人左->人右->汇点 *//* 0; 1~P; P+1~P*2; P*2+1~P*2+N; P*2+N+1~P*2+N*2; P*2+N*2+1 */for (int i = 1; i <= P; ++i) {scanf(" %d", &s);G[0][i] += s;G[i][i+P] += s;for (int j = 1; j <= s; ++j) {scanf(" %d", &t);G[i+P][(P<<1)+t] = 1;}}for (int i = 1; i <= N; ++i) {G[(P<<1)+i][(P<<1)+N+i] = P;G[(P<<1)+N+i][all] = 1;}puts(dinic(0,all)==P?"YES":"NO");}return 0;}#endif


0 0
原创粉丝点击