NYOJ 489-哭泣天使:最大流

25 篇文章 0 订阅
22 篇文章 0 订阅

点击打开链接

哭泣天使

时间限制: 1000 ms  |  内存限制: 65535 KB
难度: 5
描述

Doctor Who乘着Tardis带着Amy来到了一个星球,一开Tadis大门,发现这个星球上有个壮观的石像群,全是一些天使石像,有的石像在哭泣,有的石像像在微笑,共有m行n列,Doctor用“音速起子”扫描了一下整个石像群,得到了每行天使中在哭泣的天使的个数。当他与Amy在这里行走了一段时间之后,Doctor忽然想起了什么,怀疑这些石像是不是传说中的一种黑暗生物——“哭泣天使”——一种看似石像,却会在人不看它的时候移动,会强制把人送回某个过去的时间点,并借此汲取时间能量的生物。Doctor可不想自己和Amy迷失在一个未知的时间点里,于是Doctor立刻用“音速起子”又扫描了整个石像群,想再看看每行的在哭泣的天使个数与刚才是否相符,但是,越急就越容易出错,他一不小心扫描错了,扫描出了每列中哭泣的天使的个数。现在,由于音速起子的能量不足了,他不能够再次扫描,他想根据已有的数据判断出是否有天使改变了自己的表情,从哭泣变成不哭泣或者从不哭泣变成哭泣了。

输入
第一行是一个整数T,表示共有T组测试数据(T<=50)
每组测试数据第一行是两个整数m,n(0<m,n<=300)分别表示行数和列数
随后的两行分别有m个数和n个数分别表示对应m行中哭泣的天使石像的个数与对应n个列中哭泣的天使石像的个数。
输出
如果能根据已有信息判断出必然有石像改变了表情,则输出Terrible
如果根据已有信息无法确定石像发生了改变,则输出Not Sure (有时,你确定两次扫描时状态相同,但由于不确定之间是否发生过改变,故也输出Not Sure)
样例输入
2
2 3
1 1
1 1 0
3 3
0 1 2
3 0 0
样例输出
Not Sure
Terrible

这个题是一个二分图最大流问题,每行是一个点,列是一个点,那么左边的点全是行,右边的点全是列,所以每个行节点都有一条连向每个列节点的边,容量为1(某行某列就确定这个点了)人为添加一个源点和一个汇点,假如第 i 行的和为sum 那么源点流向编号为 i 的行节点的容量为sum,假如第 j 列的和为sum,那么第j个列节点流向汇点的容量为sum,这样就转化成了在图上求最大流的问题,如果求出来的最大流不等于题目输入时哭泣天使的总数,那么说明有天使改变了状态,输出Terrible,否则输出Not Sure,另外,如果输入的数据行和列的和都不一样,那么不需要判断,必然有天使改变状态,直接输出Terrible;

一开始最大流直接套用的书上的dinic算法,结果超时,以为这个题不能用最大流做,后来在网上找到了另一个模板,110MS过了,而且放在“月老的难题”那个题中也比之前的最大流模板快150MS,看来上交的模板也不见得就一定好嘛,但是现在这个网络流模板的正确性还有需要进一步验证,没有了深搜和广搜,看不太懂怎么求出来的增广路的,反正觉得这个人挺厉害的,也是因为我比较菜,对网络流这方面理解不深入。上代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int min(int a, int b)
{
	return a > b ? b : a;
}

const int inf = 0xfffffff;
#define clr(arr,v) memset(arr,v,sizeof(arr))

template<int MaxV,int MaxE>
class MaxFlow{
public:
	int GetMaxFlow(int s,int t,int n)  //n±íʾÓÐn¸öµã£¬sΪԴµã£¬tΪ»ãµã 
	{
		int maxflow = 0,minflow = inf,cur = s;
		Cnt[0] = n;
		memcpy(Cur,H,sizeof(H));
		while(Gap[cur] <= n)
		{
			int &i = Cur[cur];
			for(;i != -1;i = Next[i])
			{
				if(Cap[i]-Flow[i] > 0 && Gap[cur]-Gap[ Num[i] ] == 1)
				{
					pre_edge[ Num[i] ] = i;
					cur = Num[i];
					minflow = min(minflow,Cap[i]-Flow[i]);
					if(cur == t)
					{
						maxflow += minflow;
						while(cur != s)
						{
							Flow[ pre_edge[cur] ] += minflow;
							Flow[ pre_edge[cur]^1 ] -= minflow;
							cur = Num[ pre_edge[cur]^1 ];
						}
						minflow = inf;
					}
					break;
				}
			}
			if(i == -1)
			{
				if(--Cnt[ Gap[cur] ] == 0) return maxflow;
				Gap[cur] = inf;
				i = H[cur];
				for(int j = H[cur];j != -1;j = Next[j])
					if(Cap[j] > Flow[j] && Gap[ Num[j] ] < Gap[cur])
						Gap[cur] = Gap[ Num[j] ];
				if(Gap[cur] != inf) ++Cnt[ ++Gap[cur] ];
				cur = s;
			}
		}
		return maxflow;
	}
	void add(int u,int v,int flow)
	{
		Num[pos] = v;
		Cap[pos] = flow;
		Next[pos] = H[u];
		H[u] = pos++;

		Num[pos] = u;
		Cap[pos] = 0;
		Next[pos] = H[v];
		H[v] = pos++;
	}
	void clear()
	{
		clr(H,-1); clr(Flow,0); clr(Cnt,0);
		clr(Gap,0); pos = 0;
	}
private:
	int H[MaxV],Cur[MaxV],Num[MaxE],Next[MaxE];
	int Cap[MaxE],Flow[MaxE],Cnt[MaxV];
	int Gap[MaxV],pre_edge[MaxE],pos;
};

MaxFlow<700,200000> g;

int main()
{
//	freopen("test.txt", "r", stdin);
	int t;
	scanf("%d", &t);
	while(t--)
	{
		g.clear();
		int m, n;
		scanf("%d%d", &m, &n);
		int i;
		int num;
		int sum1, sum2;
		sum1 = sum2 = 0;
		for(i = 1; i < m + 1; i++)
		{
			scanf("%d", &num);
			sum1 += num ;
			g.add(0, i, num);
			int j;
			for(j = 0; j < n; j++)
			{
				g.add(i, j + m + 1, 1);
			}
		}
		for(i = m + 1; i < m + n + 1; i++)
		{
			scanf("%d", &num);
			sum2 += num;
			g.add(i, m + n + 1, num);
		}
		if(sum1 != sum2)
			printf("Terrible\n");
		else if(g.GetMaxFlow(0, m + n + 1, m + n + 2) != sum1)
			printf("Terrible\n");
		else
			printf("Not Sure\n");
//		cout<<g.GetMaxFlow(0, m + n + 1)<<endl;
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

勇敢的炮灰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值