【HDU4560 2013西山居复赛D】【二分答案+网络流拆点】我是歌手 安排演唱会_每人歌不同_每场歌不同_人歌匹配一次

原创 2015年11月20日 11:11:53


我是歌手

Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 350    Accepted Submission(s): 124


Problem Description
2013年一开始,一档音乐节目“我是歌手”就惊艳了大家一回。闲话少说,现在,你成为了这档节目的总导演,你的任务很简单,安排每一期节目的内容。

现在有N个歌手,M种歌曲流派(Rock,Pop之类),每个歌手都有自己擅长的流派领域,这些资料都已整理好。你的工作是,安排尽可能多场的演唱比赛。每一场比赛所有歌手都必须上场,为了提高收视率,每个人演唱的歌曲类型不能相同,即便一些歌手要被迫选择一些他们不擅长的。同时,为了展现全面性,在不同的演唱比赛上,每个歌手都会安排不同的歌曲流派。

但是问题是,对于任何一个歌曲流派的歌迷,如果超过K个不擅长的歌手演唱了这种歌曲,他们就会表示不满,比如,发一些宣泄不满的帖子微博,为了表示观点挑起事端等等。你当然不希望这些事情与你的节目有关,在这个前提下,你可以任意安排尽可能多的比赛场次。
 

Input
输入第一行为T,表示有T组测试数据。
每组数据以四个数字N,M,L, K开始。L表示有L组擅长关系,接下来的L行,每一行有两个数字Ai,Bi,表示歌手Ai擅长Bi类型的歌曲。

[Technical Specification]

1. 1 <= T <= 100
2. 1 <= N <= M <= 74, 0 <= K <= N
3. 0 <= L <= N*M
4. 1 <= Ai <= N, 1 <= Bi <= M, 相同关系不会重复出现
 

Output
对每组数据,先输出为第几组数据,然后输出最多比赛场次。
 

Sample Input
3 1 1 1 0 1 1 1 3 0 1 3 3 5 1 1 1 1 2 2 2 2 3 3 1
 

Sample Output
Case 1: 1 Case 2: 3 Case 3: 2
Hint
对第三组样例,可以如此安排: 第一场三位歌手分别演唱(2,3,1)类型的歌曲,第二场分别演唱(1,2,3)。 这样只有类型3被不擅长的歌手演唱过1次,挑剔的歌迷观众还可以接受。
 

Source
 


#include<stdio.h>
#include<iostream>
#include<string.h>
#include<ctype.h>
#include<math.h>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<string>
#include<algorithm>
#include<time.h>
#include<bitset>
using namespace std;
void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T> inline void gmax(T &a,T b){if(b>a)a=b;}
template <class T> inline void gmin(T &a,T b){if(b<a)a=b;}
const int N=240,M=14000,Z=1e9+7,maxint=2147483647,ms31=522133279,ms63=1061109567,ms127=2139062143;const double eps=1e-8,PI=acos(-1.0);//.0
int casenum,casei;
int id,ST,ED;
int n,m,g,L,x,y;
int first[N],w[M],cap[M],nxt[M];
bool e[80][80];
void ins(int x,int y,int cap_)
{
	id++;
	w[id]=y;
	cap[id]=cap_;
	nxt[id]=first[x];
	first[x]=id;
	id++;
	w[id]=x;
	cap[id]=0;
	nxt[id]=first[y];
	first[y]=id;
}
int d[N];
bool bfs()
{
	MS(d,-1);d[ST]=0;
	queue<int>q;q.push(ST);
	while(!q.empty())
	{
		int x=q.front();q.pop();
		for(int z=first[x];z;z=nxt[z])if(cap[z])
		{
			int y=w[z];
			if(d[y]==-1)
			{
				d[y]=d[x]+1;
				if(y==ED)return 1;
				q.push(y);
			}
		}
	}
	return 0;
}
int dfs(int x,int all)
{
	if(x==ED)return all;
	int use=0;
	for(int z=first[x];z;z=nxt[z])if(cap[z])
	{
		int y=w[z];
		if(d[y]==d[x]+1)
		{
			int tmp=dfs(y,min(cap[z],all-use));
			cap[z]-=tmp;
			cap[z^1]+=tmp;
			use+=tmp;
			if(use==all)break;
		}
	}
	if(use==0)d[x]=-1;
	return use;
}
int dinic(int flow)
{
	int tmp=0;
	while(bfs())tmp+=dfs(ST,flow);
	return tmp;
}
void build(int mid)
{
	MS(first,0);id=1;
	for(int i=1;i<=m;i++)ins(n+i,n+m+i,L);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(e[i][j])ins(i,n+m+j,1);
			else ins(i,n+j,1);
		}
	}
	ST=0;
	ED=n+m+m+1;
	for(int i=1;i<=n;i++)ins(ST,i,mid);
	for(int i=1;i<=m;i++)ins(n+m+i,ED,mid);
}
int main()
{
	scanf("%d",&casenum);
	for(casei=1;casei<=casenum;casei++)
	{
		scanf("%d%d%d%d",&n,&m,&g,&L);
		MS(e,0);
		while(g--)
		{
			scanf("%d%d",&x,&y);
			e[x][y]=1;
		}		
		int l=0;
		int r=m;
		while(l<r)
		{
			int mid=(l+r+1)>>1;
			build(mid);
			if(dinic(mid*n)==mid*n)l=mid;
			else r=mid-1;
		}
		printf("Case %d: %d\n",casei,l);
	}
	return 0;
}
/*
【trick&&吐槽】
1,最大能安排的演唱会次数是多少呢?
	只看一个人,这个人最多每首歌唱一次,所以ans不会超过m。
2,网络流每次跑都要重新建图。
3,初始化是在每次建图之前都要进行的。

【题意】
有n(n<=74)个歌手,有m(m<=74)种类型的歌曲。有g(g<=n*m)个擅长关系(x,y),告诉你x擅长歌曲y。
然后我们希望安排数量尽可能多场演唱会。
在每场演唱会中,每个歌手唱不同类型的歌曲。
这样可能导致有些歌手去唱他不擅长的歌曲。
对于任一种歌曲,被不合适唱的次数都不能超过L(0<=L<=n*m)。
问你最多能安排多少场演唱会。

【类型】
网络流

【分析】
这题涉及到分配,于是我们自然会想到网络流。

不过每首歌的演唱类型有两种,处理起来很难。
因为我们先简化问题——
如果只安排每个人唱自己擅长的音乐,那么最多能安排多少场演唱会呢?
我们只要每个人向自己擅长的音乐连一条容量为1的边。

然后假设安排k场演唱会的话。
我们从超级源点向每个人连容量为k的边。
然后从所有音乐向超级汇点连容量为k的边。
然后跑一遍最大流,看看是否能满足,最大流=k*n。
可以说明k是可行的,且k满足二分性质。

回归问题——现在每个人还可以选择演唱自己不喜欢的音乐,
这个要怎么办呢?拆点!
我们把每首音乐拆成2个点x和y,内部连接流量为L的边。
然后如果某个人擅长这首音乐,就把这个人连向y。
如果此人不擅长这首音乐,就把这个人连向x,这样我们就会记录这首音乐被不娴熟演唱的次数。

这样我们总能实现匹配关系,而且这样我们还能限制每种音乐最大被不娴熟演唱的次数。
而且通过二分(或者暴力?2333),我们还可以限制每个人表演的场次。
这样就不会有任何冲突情况出现,就可以顺利AC啦!

【时间复杂度&&优化】
点数最大为n+m+m+2,大概240即可。
边数最多为(n*m+m+n+m)*2,大概14000即可。

*/


版权声明:题解中哪里写错请一定要指出来QwQ 转载还请注明下出处哦,谢谢^_^

2013 hdu 西山居复赛(1) HDU 4557 非诚勿扰 HDU 4558 剑侠情缘 HDU 4559 涂色游戏 HDU 4560

转载 http://www.cnblogs.com/kuangbin/archive/2013/05/24/3097767.html 手 今天的比赛。。。。。速度还是慢了。。。 无缘无故错误...

2013金山西山居创意游戏程序挑战赛——复赛(1) 我是歌手

最大流、二分查找 由题意可推断出最大场数不超过M,假设分配给每个人x场,如果满足K值的限制,则最后歌曲的排列组合肯定能达到每场每个人演唱的歌曲类型不相同 如果x场能满足上述条件,则y(y 套sap的模...
  • solotzg
  • solotzg
  • 2013年05月28日 16:55
  • 603

HDU4560 我是歌手 网络流

HDU4560 我是歌手 二分+网络流。 这是金山复赛1的第四题。 最近把我是歌手小看了一些,唱功果然才是歌手的必备技能啊。 (还有观众好假啊。。) 扯远了。 这么个题目,看了...

2013金山西山居创意游戏程序挑战赛——复赛(1) 非诚勿扰

非诚勿扰 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Total Sub...
  • appte
  • appte
  • 2013年05月24日 21:09
  • 2175

2013金山西山居创意游戏程序挑战赛——复赛(1)非诚勿扰

非诚勿扰Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Total Submis...

HDU 4547 CD操作 2013金山西山居创意游戏程序挑战赛——初赛(1)

CD操作 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Total Su...

2013金山西山居创意游戏程序挑战赛——初赛(2) C 卡片游戏 (hdu 4550 )

http://acm.hdu.edu.cn/showproblem.php?pid=4550 题意: 小明最近宅在家里无聊,于是他发明了一种有趣的游戏,游戏道具是N张叠在一起的卡片,每张卡...

2013金山西山居创意游戏程序挑战赛——初赛(3) 1001 生日猜猜猜 hdu 4551

生日猜猜猜 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Total Su...

2013金山西山居创意游戏程序挑战赛——初赛(1)

/* 魔法串 模拟题,两字符串从前到后扫描, 如果当前字符相等,两字符同时向后移动一位 否则,b字符是否能转化成前一个字符 直到结束 */ #include...

金山西山居2013程序挑战赛初赛 第一题

Problem Description   小明和他的好朋友小西在玩一个新的游戏,由小西给出一个由小写字母构成的字符串,小明给出另一个比小西更长的字符串,也由小写字母组成,如果能通过魔法转换...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【HDU4560 2013西山居复赛D】【二分答案+网络流拆点】我是歌手 安排演唱会_每人歌不同_每场歌不同_人歌匹配一次
举报原因:
原因补充:

(最多只允许输入30个字)