这道题本身就是一个**“建模 + 贪心统计”的经典题型
一、题意重新梳理(把问题“翻译成人话”)
- 教室是一个 n 行 m 列 的网格同学坐在 (i, j)。
- 可以修:k 条 横向通道(在第 ai 行和 ai+1 行之间)l 条 纵向通道(在第 bi 列和 bi+1 列之间)
- 有 d 对“经常交头接耳”的同学每一对 一定是相邻的(上下 or 左右)
- 如果一条通道正好修在这两个人之间,就能把这对人隔开
👉 目标是:选择 k 条横通道 + l 条纵通道,使得“被隔开的交头接耳对”尽可能多(等价于:没被隔开的尽量少)
题目保证:最优方案唯一
二、核心建模
1️⃣ 每一对交头接耳,贡献给“某一条通道”
- 如果两个人 左右相邻:(x, y) 和 (x, y+1)那么只要在 第 y 列和 y+1 列之间修纵向通道,就能隔开👉 这对人 只和这一条纵向通道有关
- 如果两个人 上下相邻:(x, y) 和 (x+1, y)那么只要在 第 x 行和 x+1 行之间修横向通道,就能隔开👉 这对人 只和这一条横向通道有关
⚠️ 非常关键的一点:每一对交头接耳,只能被“唯一的一条通道”隔开
2️⃣ 问题转化成了什么?
- 对每一条“行间通道”,统计:有多少对人是靠这条通道才能被隔开的
- 对每一条“列间通道”,统计:有多少对人是靠这条通道才能被隔开的
然后:
从所有行间通道中,选出
k条“贡献最大的”从所有列间通道中,选出l条“贡献最大的”
这就是一个独立的贪心选择问题。
三、数据结构设计
struct jo
{
int num , z ;
};
含义:
num: 这条通道能隔开的“交头接耳对”的数量z: 通道编号(第几行 / 第几列)
两个数组分别统计:
vector<jo> a1(n + 1); // 行间通道(横向) vector<jo> a2(m + 1); // 列间通道(纵向)
初始化编号:
for(int i = 1 ; i <= n ; i++) a1[i].z = i; for(int i = 1 ; i <= m ; i++) a2[i].z = i;
四、统计每条通道的“价值”
while(d--)
{
int x1 , y1 , x2 , y2 ;
cin >> x1 >> y1 >> x2 >> y2 ;
if(x1 == x2)
{
// 左右相邻 → 纵向通道
a2[min(y1 , y2)].num++;
}
else if(y1 == y2)
{
// 上下相邻 → 横向通道
a1[min(x1 , x2)].num++;
}
}
解释得非常重要:
min(y1, y2)表示“第 y 列和 y+1 列之间”的那条纵向通道min(x1, x2)表示“第 x 行和 x+1 行之间”的那条横向通道
👉 每对人 只给一条通道 +1
五、贪心选择(题目保证最优唯一)
1️⃣ 按贡献从大到小排序
sort(all(a1), cmp); sort(all(a2), cmp);
cmp:
bool cmp(const jo& a , const jo& b)
{
return a.num > b.num;
}
2️⃣ 取前 k / l 条
for(int i = 0 ; i < k ; i++) ans1.push_back(a1[i]); for(int i = 0 ; i < l ; i++) ans2.push_back(a2[i]);
3️⃣ 按编号升序输出(题目格式要求)
sort(all(ans1), cmp1); sort(all(ans2), cmp1);
bool cmp1(const jo & a , const jo & b)
{
return a.z < b.z;
}
六、输出部分
for(int i = 0 ; i < k ; i++)
{
cout << ans1[i].z;
if(i != k -1) cout << " ";
}
cout << endl;
for(int i = 0 ; i < l ; i++)
{
cout << ans2[i].z;
if(i != l - 1) cout << " ";
}
严格符合题目输出格式:
- 第一行:横向通道行号
- 第二行:纵向通道列号
七、复杂度分析
- 统计:O(d)
- 排序:行:O(n log n)列:O(m log m)
- n, m ≤ 1000,完全没压力
总代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
#define endl '\n'
#define all(a) a.begin(), a.end()
struct jo
{
int num , z ;
};
bool cmp(const jo& a , const jo& b)
{
return a.num > b.num;
}
bool cmp1(const jo & a , const jo & b)
{
return a.z < b.z;
}
void work()
{
int n , m , k , l , d ; cin >> n >> m >> k >> l >> d ;
vector<jo>a1(n + 1 , {0 , 0});
vector<jo>a2(m + 1 , {0 , 0});
for(int i = 1 ; i <= n ; i++)
{
a1[i].z = i ;
}
for(int i = 1 ; i <= m ; i++)
{
a2[i].z = i ;
}
while(d--)
{
int x1 , y1 , x2 , y2 ; cin >> x1 >> y1 >> x2 >> y2 ;
if(x1 == x2)
{
a2[min(y1 , y2)].num++;
}
else if(y1 == y2)
{
a1[min(x1 , x2)].num++;
}
}
sort(all(a1) , cmp);
sort(all(a2) , cmp);
vector<jo>ans1;
vector<jo>ans2;
for(int i = 0 ; i < k ; i++)
{
ans1.push_back(a1[i]);
}
for(int i = 0 ; i < l ; i++)
{
ans2.push_back(a2[i]);
}
sort(all(ans1),cmp1);
sort(all(ans2),cmp1);
for(int i = 0 ; i < k ; i++)
{
cout << ans1[i].z ;
if(i != k -1)
{
cout << " " ;
}
}
cout << endl ;
for(int i = 0 ; i < l ; i++)
{
cout << ans2[i].z ;
if(i != l - 1)
{
cout << " " ;
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t = 1;
while (t--)
{
work();
}
return 0;
}

京公网安备 11010502036488号