解题报告 之 SGU242 Student's Morning

解题报告 之 SGU242 Student's Morning


Description


One Monday morning after some very fun party N students woke up at the flat of one of them. Notice that it was a Monday morning and every student of that party needs to be in his university this day. But nobody wants to go to his university alone (there were students from different universities). So, they decided to select from all universities only K of them to visit. Every selected university must be visited by at least two of the students. Every student has his own preference list of universities. It means, if some university is in list of some student's preferred universities, this student can go to this university with some non-empty company of students. Notice, that some of students can stay at the flat and continue drinking "juices" and playing "games". For example, student Shokman was to stay home (due to failed exam) with foreign student Chokman, who remained home because of runny nose. 
In that problem there are no preferences between students, because if they have very fun party that already means that everyone of them prefers anybody from this company. 

More formally, your task is, given numbers of students, selected universities and preference list of every student, to decide whether it is possible to visit all universities by at least two of students or no, and if it is possible you must output for each university numbers of students, which have to go to it in one company. One student can't be in more than one company.

Input
First line of input file contains two numbers N and K (0<=K<=N<=200). Next N lines contain preference lists of each student. Every preference list is started by number of preferred universities followed by numbers of these universities.

Output
First line of output file must contain word "YES" (without quotes), if it possible to visit all universities, satisfying rules of that task or word "NO" (also without quotes) when it is impossible. In case of positive answer next K lines must contain lists of students, who are going to corresponding university. First number in list of students must be a number of students in the list, followed by numbers of these students.

Sample test(s)

Input
 
   
4 2 1 1 2 1 2 1 2 2 1 2 3 2 2 1 2 2 1 2 2 1 2
Output
 
   

YES 
2 1 2 
2 3 4  
NO


题目大意:好好一道水题你搞什么阅读理解! n个学生m个大学,每个学生有自己想去的大学,一个学生最多去一个大学(也可不去),每个大学至少要有两个人去,问是否是一种方案满足?如果有则输出任意一种满足的方案。

分析:有点像二分图匹配,然后用满流判断是否满足,再加上残余网络法输出方案。具体思路如下,超级源点与每个学生相连,负载为1,每个大学与超级汇点相连,负载为2。每个学生与要去的每所大学相连,负载都为1。然后跑最大流,如果满足max_flow==m*2,表示有解。此时扫描残余网络中与学生相连的每条边,如果不是与src相连的边且负载减小了(说明该学生去了该学校),则加入到对应大学的list中,最后输出list完事儿。另外此题内存稍小,所以必须控制好边数,大概是n*m多一点儿。

上代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;

const int MAXN = 520;
const int MAXM = 100000;
const int INF = 0x3f3f3f3f;

struct Edge
{
	int from, to, cap, next, oricap;
};


Edge edge[MAXM];
int head[MAXN];
int stu[405][405];
int level[MAXN];
int src, des, cnt;


void addedge( int from, int to, int cap )
{
	edge[cnt].from = from;
	edge[cnt].to = to;
	edge[cnt].cap = cap;
	edge[cnt].oricap = cap;
	edge[cnt].next = head[from];
	head[from] = cnt++;

	swap( from, to );

	edge[cnt].from = from;
	edge[cnt].to = to;
	edge[cnt].cap = 0;
	edge[cnt].oricap = 0;
	edge[cnt].next = head[from];
	head[from] = cnt++;
}

int bfs( )
{
	memset( level, -1, sizeof level );
	queue<int> q;
	while (!q.empty( ))
		q.pop( );

	level[src] = 0;
	q.push( src );

	while (!q.empty( ))
	{
		int u = q.front( );
		q.pop( );

		for (int i = head[u]; i != -1; i = edge[i].next)
		{
			int v = edge[i].to;
			if (edge[i].cap > 0 && level[v] == -1)
			{
				level[v] = level[u] + 1;
				q.push( v );
			}
		}
	}
	return level[des] != -1;
}

int dfs( int u, int f )
{
	if (u == des)	return f;
	int tem;
	for (int i = head[u]; i != -1; i = edge[i].next)
	{
		int v = edge[i].to;
		if (edge[i].cap > 0 && level[v] == level[u] + 1)
		{
			tem = dfs( v, min( f, edge[i].cap ) );
			if (tem > 0)
			{
				edge[i].cap -= tem;
				edge[i ^ 1].cap += tem;
				return tem;
			}
		}
	}
	level[u] = -1;
	return 0;
}

int Dinic( )
{
	int ans = 0, tem;
	while (bfs( ))
	{
		while ((tem = dfs( src, INF )) > 0)
		{
			ans += tem;
		}
	}
	return ans;
}

int main( )
{
	int n, m;
	src = 0; des = 510;
	string str;
	while (cin >> n >> m)
	{
		memset( head, -1, sizeof head );
		cnt = 0;
		for (int i = 1; i <= n; i++)
		{
			addedge( src, i, 1 );
			int unum;
			cin >> unum;
			for (int j = 1; j <= unum; j++)
			{
				int u;
				cin >> u;
				addedge( i, u + 200, 1 );
			}
		}

		for (int i = 1; i <= m; i++)
		{
			addedge( i + 200, des, 2 );
			stu[i + 200][0] = 0;
		}
		if (Dinic( ) < m * 2)
		{
			cout << "NO" << endl;
			continue;
		}
		for (int i = 1; i <= n; i++)
		{
			for (int j = head[i]; j != -1; j = edge[j].next)
			{
				if (edge[j].to != src&&edge[j].oricap>edge[j].cap)
				{
					stu[edge[j].to][++stu[edge[j].to][0]] = i;
				}
			}
		}

		cout << "YES" << endl;
		for (int i = 1; i <= m; i++)
		{
			cout << stu[i + 200][0] << " ";
			for (int j = 1; j < stu[i + 200][0]; j++)
			{
				cout << stu[i + 200][j] << " ";
			}
			cout << stu[i + 200][stu[i + 200][0]] << endl;
		}
	}

	return 0;
}

好吧。。。祝愿信息安全大赛成功,虽然我是抱大腿的哈哈哈哈哈哈哈哈BJ4.。。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值