哭泣天使
-
描述
-
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
-
第一行是一个整数T,表示共有T组测试数据(T<=50)
这个题是一个二分图最大流问题,每行是一个点,列是一个点,那么左边的点全是行,右边的点全是列,所以每个行节点都有一条连向每个列节点的边,容量为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;
}