8.26下午二分与深搜测试
比赛传送门
分数情况
P2249 【深基13.例1】查找 | P1706 全排列问题 | P8647 [蓝桥杯 2017 省 AB] 分巧克力 | P2440 木材加工 | B3624 猫粮规划 | P2105 K皇后 | P3853 路标设置 | P3743 小鸟的设备 |
---|---|---|---|---|---|---|---|
0 | 100 | 12 | 100 | 0 | 0 | 0 | 15 |
T1. P2249 【深基13.例1】查找
题目传送门
\(Wrong\) \(Code:\)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 7;
int n, m;
int a[maxn];
int chenlaoshi(int ans)
{
int l = 1 - 1;
int r = n + 1;
a[l] = INT_MIN;
a[r] = INT_MAX;
int mid;
while (l + 1 < r)
{
mid = (l + r) / 2;
if (a[mid] < ans)
{
l = mid;
}
if (a[mid] > ans)
{
r = mid;
}
if (a[mid] == ans)
{
r = mid;
}
}
if (a[r] == ans)
{
return r;
}
return -1;
}
signed main()
{
cin >> n >> m;
for (int i = 1; i <= n; ++i)
{
cin >> a[i];
}
for (int i = 1; i <= m; ++i)
{
int x;
cin >> x;
cout << chenlaoshi(x) << ' ';
}
return 0;
}
错误原因
数组开小了
\(1 \leq n \leq 10^6\)
不能是
1e5 + 7
要到
1e6 + 7
问题分析
二分查找相等数值的左侧边界
如果查找值在中间值的左边,需要继续查找左侧边界
如果查找值在中间值的右边,需要继续查找右侧边界
如果查找值和中间值相等,也是需要继续查找左侧边界
\(AC\) \(Code:\)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 7;
int a[maxn];
int n;
int chenlaoshi(int x)
{
int l = 1 - 1;
int r = n + 1;
a[0] = -1e9;
a[n + 1] = 1e9;
while (l + 1 < r)
{
int mid = (l + r) / 2;
if (a[mid] > x)
{
r = mid;
}
if (a[mid] < x)
{
l = mid;
}
if (a[mid] == x)
{
r = mid;
}
}
if (a[r] == x)
{
return r;
}
return -1;
}
int main()
{
cin >> n;
int m;
cin >> m;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
while (m--)
{
int x;
cin >> x;
cout << chenlaoshi(x) << ' ';
}
return 0;
}
T2. P1706 全排列问题
题目传送门
问题分析
深搜全排列问题
非常经典
\(AC\) \(Code:\)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 7;
bool vis[maxn];
int ans[maxn];
int n;
void print()
{
for (int i = 1; i <= n; ++i)
{
cout << setw(5) << ans[i];
}
cout << "\n";
return ;
}
void dfs(int cur)
{
if (cur == n + 1)//满足条件就输出
{
print();
}
for (int i = 1; i <= n; ++i)
{
if (vis[i] == false)
{
ans[cur] = i;// 代表填写的是第cur个格子,填写的数字是i
vis[i] = true;//标记
dfs(cur + 1);
vis[i] = false;//标记
}
}
return ;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
dfs(1);
return 0;
}
T3. P8647 [蓝桥杯 2017 省 AB] 分巧克力
题目传送门
\(Wrong\) \(Code:\)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 7;
int n, k;
int h[maxn], w[maxn];
bool check(int x)
{
int sum = 0;
for (int i = 1; i <= n; ++i)
{
sum += ((h[i] / x) + (w[i] / x));
if (sum >= k)
{
return true;
}
}
return false;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> k;
int maxx = INT_MIN;
for (int i = 1; i <= n; ++i)
{
cin >> h[i] >> w[i];
maxx = max(maxx, max(h[i], w[i]));
}
int l = 1 - 1;
int r = maxx + 1;
while (l + 1 < r)
{
int mid = (l + r) / 2;
if (check(mid))
{
l = mid;
}
else
{
r = mid;
}
}
cout << l << "\n";
return 0;
}
错误原因
错误地点:第 \(12\) 行
是 \(+\) 不是 \(\times\)
问题分析
考虑边长和切分的巧克力关系
如果边长越短,切分得到的巧克力越多
边长越长,切分得到的巧克力总数越少。
这里存在单调关系,根据这种二分关系直接进行二分答 案。
\(AC\) \(Code:\)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 7;
int n, k;
int h[maxn], w[maxn];
bool check(int x)
{
int sum = 0;
for (int i = 1; i <= n; ++i)
{
sum += ((h[i] / x) * (w[i] / x));
if (sum >= k)
{
return true;
}
}
return false;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> k;
int maxx = INT_MIN;
for (int i = 1; i <= n; ++i)
{
cin >> h[i] >> w[i];
maxx = max(maxx, max(h[i], w[i]));
}
int l = 1 - 1;
int r = maxx + 1;
while (l + 1 < r)
{
int mid = (l + r) / 2;
if (check(mid))
{
l = mid;
}
else
{
r = mid;
}
}
cout << l << "\n";
return 0;
}
T4. P2440 木材加工
题目传送门
问题分析
典型的二分答案
二分长度
长度越长,切分的段数越少
长度越短,切分的段数越多
这里存在单调性关系。
\(AC\) \(Code:\)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 7;
int n, k;
int a[maxn];
bool check(int x)
{
int sum = 0;
for (int i = 1; i <= n; ++i)
{
sum += a[i] / x;
if (sum >= k)
{
return true;
}
}
return false;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> k;
if (n == 0)
{
cout << 0 << "\n";
return 0;
}
int maxx = INT_MIN;
for (int i = 1; i <= n; ++i)
{
cin >> a[i];
maxx = max(maxx, a[i]);
}
int l = 1 - 1;
int r = maxx + 1;
while (l + 1 < r)
{
int mid = (l + r) / 2;
if (check(mid))
{
l = mid;
}
else
{
r = mid;
}
}
cout << l << "\n";
return 0;
}
T5. B3624 猫粮规划
题目传送门
问题分析
在原来全排列的基础之上
添加一个剪枝方式,如果已经大于 \(r\) 的总和
后面这一段直接
return ;
\(AC\) \(Code:\)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 50;
int n, l, r;
int a[maxn];
int ans;
void dfs(int j, int k)
{
if (l <= k && k <= r)
{
ans++;
}
if (k > r || j == n)
{
return ;
}
for (int i = j + 1; i <= n; i++)
{
dfs(i, k + a[i]);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> l >> r;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
sort(a + 1, a + 1 + n);
for (int i = 1; i <= n; i++)
{
dfs(i, a[i]);
}
cout << ans << "\n";
return 0;
}
T6. P2105 K皇后
题目传送门
\(Wrong\) \(Code:\)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 5005;
int n, m, k;
bool qi[maxn][maxn];
bool gongji[maxn][maxn];
void do_gongji(int x, int y)
{
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= m; ++j)
{
if ((i == x) || (j == y) || (i == j) || (i == j + 1))
{
gongji[i][j] = 1;
}
}
}
return ;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> k;
for (int i = 1; i <= k; ++i)
{
int x, y;
cin >> x >> y;
qi[x][y] = 1;
gongji[x][y] = 1;
do_gongji(x, y);
}
int cnt = 0;
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= m; ++j)
{
if (gongji[i][j] == 0)
{
cnt++;
}
}
}
cout << cnt << "\n";
return 0;
}
\(_{_{本来想用暴力的}}\)
问题分析
考虑之前写的八皇后问题
因为本题存在多次标记的情况,直接采用深度优先搜索的方式会超时
所以切换思路,提前将行、列、对角线直接标记
然后一个个格子进行判断,最后剪枝操作如果这一行被标记了,这一行直接跳过
\(AC\) \(Code:\)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 7;
int hang[maxn];
int lie[maxn];
int zhu[maxn];
int fu[maxn];
signed main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m, k;
cin >> n >> m >> k;
int x, y;
for (int i = 1; i <= k; i++)
{
cin >> x >> y;
hang[x] = true;
lie[y] = true;
zhu[n + x - y] = true;
fu[x + y] = true;
}
int cnt = 0;
for (int i = 1; i <= n; i++)
{
if (hang[i] == true)
{
continue;
}
for (int j = 1; j <= m; j++)
{
if (lie[j] || zhu[n + i - j] || fu[i + j])
{
continue;
}
cnt++;
}
}
cout << cnt;
return 0;
}
T7. P3853 [TJOI2007] 路标设置
题目传送门
问题分析
空旷指数越大,添加的路标数量越少
但是空旷指数越小,意味着路标的密度越大,数量越多
所以此处需要获取间隔最大值的最小,比较明显的二分答案
\(AC\) \(Code:\)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 7;
int a[maxn], n, m, l;
bool check(int x)
{
int cnt = 0;
for (int i = 1; i <= m; i++)
{
cnt += (a[i] - a[i - 1] - 1) / x;
}
cnt += (n - a[m] - 1) / x;
if (cnt > l)
{
return false;
}
else
{
return true;
}
}
signed main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> l;
for (int i = 1; i <= m; i++)
{
cin >> a[i];
}
int l = 1 - 1;
int r = 1e9 + 1;
int mid;
while (l + 1 < r)
{
mid = (l + r) / 2;
if (check(mid))
{
r = mid;
}
else
{
l = mid;
}
}
cout << r;
return 0;
}
T8. P3743 小鸟的设备
题目传送门
问题分析
所有设备运⾏的时间显然有单调性,若时⻓ \(x\) 可⾏,⼩于 \(x\) 的也可⾏,若 \(x\) 不可⾏,⼤于 \(x\) 的也不可⾏。
二分 \(x\) , 重点考虑如何设计
check(mid)
。枚举每个设备,其运⾏
mid
时间需要a[i]
\(\times\)mid
的电量依靠⾃身 的电量
b[i]
若不够那么就需要⽤充电宝 充
a[i]
\(\times\)mid
\(-\)b[i]
的电量统计充电宝的需要充电的总电量
sum
sum
不应该超过p
\(\times\)mid
\(AC\) \(Code:\)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 7;
int n;
double eps = 1e-5;
double a[maxn], b[maxn], p;
bool check(double x)
{
double sum = 0;
for (int i = 1; i <= n; ++i)
{
if (b[i] < x * a[i])
{
sum += (x * a[i] - b[i]);
}
}
return sum <= x * p;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> p;
for (int i = 1; i <= n; ++i)
{
cin >> a[i] >> b[i];
}
double l = 0 - eps;
double r = 1e10 + eps;
while (l + eps < r)
{
double mid = (l + r) / 2;
if (check(mid) == 1)
{
l = mid;
}
else
{
r = mid;
}
}
if (r == 1e10 + eps)
{
cout << -1 << "\n";
}
else
{
cout << l << "\n";
}
return 0;
}