NOJ 1548 hash+网络流

题目链接:http://cdn.ac.nbutoj.com/Problem/view.xhtml?id=1548

比赛链接:http://cdn.ac.nbutoj.com/Contest/view/id/68.xhtml

题意:

1、每种书在图书馆里有且仅有一本。

2、每个人阅读一本书需要一天。

3、当一个人阅读完一本书后会使社会幸福感增加1.

4、自然,当好孩子看完书后一大早就归还图书馆,不会影响第二天借书。

第一行给出n,m,K1<=n,m<=50, 1<=K<=10)表示有n个人(编号从1-n),图书馆藏书m本(编号从1-m)。

下面n行第i行表示第i个人的借书情况,先给出两个整数 S, P1<=S,P<=50)表示第i个人的借阅时间从第S天开始借阅(借阅时间是[S,S+K-1]),对P本书感兴趣,后面P个数字代表所感兴趣的书。

输出一个整数表示能增加的最大社会幸福感,若不能使社会更幸福就输出”If you do not leave me, I will by your side until the life end!(不包括引号)

 

思路:

首先可以确定是网络流。

无责任民科表示:二分匹配较为简单,最大匹配数即是答案。(尚未找到ac代码,但我相信我民科定有O(n+m)复杂度的ac代码)

网络流:

人、书、时间三者互相约束,一天内一本书只能被一个人读。如果直接将三者hash 点数最大是n^3*k。

所以将其中两者hash出,最大点数可以降到n^2*k,而这个方法要注意在中间的因素要进行拆点,类似以下数据可以cha掉未拆点的代码:

3 2 2 
1 2 1 2
1 1 1
1 1 1

3 3 2
1 2 2 3
2 2 2 3
2 2 2 3

4 1 2
1 1 1
1 1 1
2 1 1
2 1 1

3 2 2
1 1 1
2 2 1 2
3 2 1 2
ans:
3
5
3
5



 

Note:
注意最后一天不是最迟到图书馆的lastday,而是lastday + (K-1)
 
建图:

最优的情况是每个人在K天内都阅读书,所以把人和K天hash出来,用一个点表示一个人一天的情况。

1、源点 与 人+K天 建边 

 2、人+K天 与 书+总天数(和K无关) 建边

3、书+总天数(和K无关) 与 汇点 建边

标程:

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<queue>
#include<vector>
using namespace std;

#define N 100000
#define M 1000000
#define inf 536870912
#define end End
inline int max(int a,int b){return a>b?a:b;}
struct Edge{
	int from, to, cap, nex;
}edge[M];

int head[N], edgenum;
void addedge(int u, int v, int cap){
	Edge E = { u, v, cap, head[u]};
	edge[ edgenum ] = E;
	head[u] = edgenum ++;

	Edge E2= { v, u, 0,   head[v]};
	edge[ edgenum ] = E2;
	head[v] = edgenum ++;
}
int sign[N], s, t;
bool BFS(int from, int to){
	memset(sign, -1, sizeof(sign));
	sign[from] = 0;

	queue<int>q;
	q.push(from);
	while( !q.empty() ){
		int u = q.front(); q.pop();
		for(int i = head[u]; i!=-1; i = edge[i].nex)
		{
			int v = edge[i].to;
			if(sign[v]==-1 && edge[i].cap)
			{
				sign[v] = sign[u] + 1, q.push(v);
				if(sign[to] != -1)return true;
			}
		}
	}
	return false;
}
int Stack[M], top, cur[M];
int dinic(){

	int ans = 0, i;
	while( BFS(s, t) )
	{
		memcpy(cur, head, sizeof(head));
		int u = s;		top = 0;
		while(1)
		{
			if(u == t)
			{
				int flow = inf, loc;
				for(i = 0; i < top; i++)
					if(flow > edge[ Stack[i] ].cap)
					{
						flow = edge[Stack[i]].cap;
						loc = i;
					}

					for(i = 0; i < top; i++)
					{
						edge[ Stack[i] ].cap -= flow;
						edge[Stack[i]^1].cap += flow;
					}
					ans += flow;
					top = loc;
					u = edge[Stack[top]].from;
			}
			for(i = cur[u]; i!=-1; cur[u] = i = edge[i].nex)
				if(edge[i].cap && (sign[u] + 1 == sign[ edge[i].to ]))break;
			if(cur[u] != -1)
			{
				Stack[top++] = cur[u];
				u = edge[ cur[u] ].to;
			}
			else
			{
				if( top == 0 )break;
				sign[u] = -1;
				u = edge[ Stack[--top] ].from;
			}
		}
	}
	return ans;
}
int n, m, K;
int from[101], lastday;
vector<int>G[101];
int peo_day(int peo, int day){
	return (peo-1)*K+day;
}
int book_day1(int book, int day){
	return (book-1)*lastday+day + n*K;
}
int book_day2(int book, int day){
	return (book-1)*lastday+day + lastday*m + n*K;
}
void init(){
	memset(head, -1, sizeof(head)); edgenum = 0;
	for(int i = 1; i <= n; i++)G[i].clear();
	s = 0;
	lastday = 0;
}
int main(){
	int i, j, k;
	while(~scanf("%d %d %d", &n, &m, &K)){
		init();
		for(i = 1; i <= n; i++)
		{
			int booknum; scanf("%d %d",&from[i],&booknum);
			while(booknum--)scanf("%d",&j), G[i].push_back(j);
			lastday = max(lastday, from[i]);
		}
		lastday += K-1;
		t = 20000;
		for(i = 1; i <= n; i++)
			for(j = 1; j <= K; j++)
			{
				addedge(s, peo_day(i,j), 1);						//源点   - 人+K天
				for(int z = 0; z < G[i].size(); z++)
					addedge(peo_day(i,j), book_day1(G[i][z], j+from[i]-1), 1);
			}
		for(i = 1; i <= m; i++)
			for(j = 1; j <= lastday; j++)
				addedge(book_day1(i,j), book_day2(i,j), 1), addedge(book_day2(i,j), t, 1);

		printf("%d\n", dinic());
	}
	return 0;	
}



 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值