题目描述
农夫约翰有一个农场,他把上面分成了n×m个格子,每个格子里养了一头牛。 但是有p对相邻格子(有共同边的格子)的牛是敌对关系,喜欢打架,约翰想做一些栅栏把他们分开。 所有的栅栏都是沿着边线的,横的或者竖的,每条栅栏都是贯穿整个横边或者竖边的。 但是约翰的预算有限,他想知道最多建k条栅栏使得尽可能减少打架的牛的对数。 比如下图中,相同的字符表示会相互打架的牛,我们可以建3条栅栏(红线),使得所有敌对关系的牛都隔离开。
输入
第一行是一个整数T(1≤T≤400),表示样例的个数。 每个样例的第一行是4个整数n(1≤n≤100),m(1≤m≤100),p(1≤p≤2nm−n−m),k(1≤k≤max(1,n+m−2))。 以后的p行,每行4个整数x1,y1,x2,y2(1≤x1,x2≤m;1≤y1,y2≤n),表示(x1,y1)与(x2,y2)的牛需要分开。输入保证没有重复信息并且输入的都是相邻格。
输出
每行输出一个样例的结果,输出在建立k条栅栏的情况下,还会打架的牛的对数。如果可以完全分隔开牛,那么还需要输出需要建立的最少的栅栏数。
样例输入
2 5 5 3 4 1 2 2 2 2 3 3 3 2 4 2 5 5 5 3 1 1 2 2 2 2 3 3 3 2 4 2 5样例输出
0 3 2样例解释
输入的样例即图示,明显第一个样例中只需要3条栅栏就能把所有敌对的牛分开;第二个样例只能分开1对牛,所以还剩2对敌对关系的牛。
提示
巨大的输入量,请使用C风格的输入。
解题思路:
- 有 n*m 个格子,所以有 n-1 个行栅栏,有 m-1 个列栅栏,每有一对敌对牛在某一栅栏两侧,即在该处栅栏,记录 敌对牛数量 ++ .
- 把各行、列 记录的数量规整到一个数组中, 得到总共需要建造的栅栏数目。
- 如果数量小于等于 k,直接输出; 如果数量大于k,再额外处理,计算最少剩余敌对牛数量。
AC代码:
#include <stdio.h>
#include <stdlib.h>
int cmp(const void* p1, const void* p2){ // 降序排列
return *(int *)p2 - *(int *)p1;
}
int Min(int x,int y){
return x>y ? y : x;
}
int Max(int x,int y){
return x<y ? y : x;
}
int main()
{
int T,n,m,p,k,x1,x2,y1,y2;
scanf("%d",&T);
while (T --)
{
scanf("%d %d %d %d",&n,&m,&p,&k);
int fence[3][102] = {0}; // 第一维 0 代表行栅栏, 1 代表列栅栏
int fence_num[204] = {0}; // 存储各行各列分别有多少头 敌对牛
for (int i = 1; i <= p; i ++)
{
scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
if (x1 != x2) fence[0][Min(x1,x2)] ++; // 如果两牛上下敌对, fence[0][较小的坐标] ++
if (y1 != y2) fence[1][Min(y1,y2)] ++; // 如果两牛左右敌对, 列栅栏分隔 敌对牛数量 ++
}
int num = 0, ans = 0, maxt = Max(n,m);
for (int i = 1; i <= maxt; i ++)
{
if (fence[0][i] != 0) fence_num[num++] = fence[0][i]; // 如果该行有敌对牛, 记录下敌对牛数量
if (fence[1][i] != 0) fence_num[num++] = fence[1][i];
} // 有 num 行/列 的牛需要隔离, 即需要 num 个栅栏
if (num <= k) printf("0 %d\n",num);
else
{
qsort(fence_num,num,sizeof(int),cmp); // 排序,优先在 敌对牛数量多的地方 建造栅栏
for (int i = k; i < num; i ++) // 栅栏数量不够, 还剩下 ans 个敌对牛未隔离
ans += fence_num[i];
printf("%d\n",ans);
}
}
return 0;
}