A - 栅栏
思路:
二分 + D F S DFS DFS + 剪枝
总体思路如下:
- m i d mid mid 为最多可以剪出的木材数(升序排列)
- 按照每一个 m i d mid mid 判断是否可以完成,若 t r u e true true,则 l = m i d l = mid l=mid;反之, r = m i d − 1 r = mid - 1 r=mid−1
剪枝:
- 优化搜索顺序:木材( s m a l l small small) 从从大到小枚举,木板( b i g big big ) 从小到大枚举,产生的分支数量较少
- 可行性剪枝 :( s u m sum sum s m a l l [ m ] small[m] small[m] <= s u m sum sum b i g [ n ] big[n] big[n] )
- 可行性剪枝:若 s u m sum sum b i g big big − - − w a s t e waste waste < < < s u m sum sum s m a l l [ m i d ] small[mid] small[mid]
- 排除等效冗余: s m a l l [ k ] small[k] small[k] = s a m l l [ k − 1 ] samll[k-1] samll[k−1],继续从当前位置开始枚举
代码如下:
#include <bits/stdc++.h>
#define fast ios::sync_with_stdio(false), cin.tie(nullptr); cout.tie(nullptr)
#define x first
#define y second
#define int unsigned int
using namespace std;
typedef pair<int, int> PII;
const int N = 55, M = 1010, null = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-8;
int n, m;
int big[N], small[M], sum_small[M];
int sum_big;
int waste;
int mid;
bool dfs(int k, int now)
{
if(k == 0) return true; // 成立
if(sum_big - waste < sum_small[mid]) return false; // 3. 可行性剪枝
bool f = 0;
for (int i = now; i <= n; i ++ )
{
if(big[i] >= small[k])
{
big[i] -= small[k];
if(big[i] < small[1]) waste += big[i];
if(small[k - 1] == small[k]) f = dfs(k - 1, i); // 4. 排除等效冗余
else f = dfs(k - 1, 1);
// 恢复现场
if(big[i] < small[1]) waste -= big[i];
big[i] += small[k];
if(f) return true;
}
}
return false;
}
void solve()
{
cin >> n;
for (int i = 1; i <= n; i ++ )
cin >> big[i], sum_big += big[i];
sort(big + 1, big + n + 1);
cin >> m;
for (int i = 1; i <= m; i ++ )
cin >> small[i];
sort(small + 1, small + m + 1);
for (int i = 1; i <= m; i ++ )
sum_small[i] = sum_small[i - 1] + small[i];
while(sum_small[m] > sum_big) m --; // 2. 可行性剪枝
int l = 0, r = m;
while(l < r)
{
mid = l + r + 1>> 1;
if(dfs(mid, 1)) l = mid; // 1. 优化搜索顺序
else r = mid - 1;
}
cout << l << endl;
return;
}
signed main()
{
fast;
int T = 1;
//cin >> T;
while(T -- )
solve();
return 0;
}
B - 扑克牌
思路:
二分答案
- m i d mid mid 为所求扑克牌的套数
- 可用的 j o k e r joker joker 数目为 m i n ( m i d , m ) min(mid, m) min(mid,m) 判断是否超出所用 j o k e r joker joker 的数目
- 若没超出 l = m i d l = mid l=mid ;反之, r = m i d − 1 r = mid - 1 r=mid−1
代码如下:
#include <bits/stdc++.h>
#define fast ios::sync_with_stdio(false), cin.tie(nullptr); cout.tie(nullptr)
#define x first
#define y second
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 55, M = 1010, null = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-8;
int n, m;;
int a[N];
bool check(int x)
{
int res = 0;
int s = min(x, m);
for (int i = 1; i <= n; i ++ )
{
if(a[i] < x) res += x - a[i];
if(res > s) return false;
}
return true;
}
void solve()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ )
cin >> a[i];
int l = 1, r = 1e9;
while(l < r)
{
int mid = l + r + 1 >> 1;
if(check(mid)) l = mid;
else r = mid - 1;
}
cout << l << endl;
return;
}
signed main()
{
fast;
int T = 1;
//cin >> T;
while(T -- )
solve();
return 0;
}
C - 跳石头
思路:
二分答案
- m i d mid mid 为跳跃距离的最大值
- 若 a [ i ] a[i] a[i] 与 上一个石子 a [ l a s t ] a[last] a[last] 距离小于 m i d mid mid , r e s + + res ++ res++
- 若 r e s > m res > m res>m, m i d mid mid 过小, l = m i d l = mid l=mid;反之 r = m i d r = mid r=mid
代码如下:
#include <bits/stdc++.h>
#define fast ios::sync_with_stdio(false), cin.tie(nullptr); cout.tie(nullptr)
#define x first
#define y second
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 50010, M = 1010, null = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-8;
int n, m, len;
int a[N];
bool check(int x)
{
int res = 0;
int last = 0;
for (int i = 1; i <= n; i ++ )
{
if(a[i] - a[last] < x) res ++;
else last = i;
if(res > m) return false;
}
return true;
}
void solve()
{
cin >> len >> n >> m;
for (int i = 1; i <= n; i ++ )
cin >> a[i];
int l = 0, r = len;
while(l < r)
{
int mid = l + r + 1 >> 1;
if(check(mid)) l = mid;
else r = mid - 1;
}
cout << r << endl;
return;
}
signed main()
{
fast;
int T = 1;
//cin >> T;
while(T -- )
solve();
return 0;
}
D - 小车问题
思路:
浮点数二分答案:
- 甲的总时间为:车 t i m e time time + 人 t i m e time time;乙的总时间为:人 t i m e time time + 车 t i m e time time,若时间最短,则甲乙同时到终点
- m i d mid mid 表示 甲坐车走的距离,根据距离算甲乙的时间
- 若 甲 t i m e time time > 乙 t i m e time time , m i d mid mid 太小, l = m i d l = mid l=mid; 反之, m i d mid mid 太大, r = m i d r = mid r=mid
代码如下:
#include <bits/stdc++.h>
#define fast ios::sync_with_stdio(false), cin.tie(nullptr); cout.tie(nullptr)
#define x first
#define y second
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 50010, M = 1010, null = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-8;
int n, m, len;
int a[N];
bool check(double x)
{
double atime = x / m + (len - x) / n;
double bt = x / m + (x - x / m * n) / (m + n) ;
double btime = bt + (len - bt * n) / m;
//cout << atime << " " << btime << endl;
if(btime > atime) return true;
else return false;
}
void solve()
{
scanf("%d%d%d", &len, &n, &m);
double l = 0, r = len;
while( fabs(l - r) > eps )
{
double mid = (l + r) / 2;
if(check(mid)) r = mid;
else l = mid;
}
double res = l / m + (len - l) / n;
printf("%.2lf\n", res);
return;
}
signed main()
{
//fast;
int T = 1;
//cin >> T;
while(T -- )
solve();
return 0;
}
E - 切绳子
思路:
浮点数二分答案:
- 查找最小长度的最大值(左半区间的右端点)
- m i d mid mid 表示为绳子的长度
- 若 m i d mid mid <= a n s ans ans (即、 r e s > = m res >= m res>=m) ,此时 l l l 向右移动, l = m i d l = mid l=mid
- 反之, r = m i d r = mid r=mid
代码如下:
#include <bits/stdc++.h>
#define fast ios::sync_with_stdio(false), cin.tie(nullptr); cout.tie(nullptr)
#define x first
#define y second
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 10010, M = 1010, null = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
int n, m, len;
double a[N];
bool check(double x)
{
int res = 0;
for (int i = 1; i <= n; i ++ )
{
res += floor(a[i] / x);
}
if(res >= m) return true;
else return false;
}
void solve()
{
scanf("%lld %lld", &n, &m);
for (int i = 1; i <= n; i ++ )
scanf("%lf", &a[i]);
double l = 0, r = 1e9;
while( fabs(r - l) > eps )
{
double mid = (l + r) / 2;
if(check(mid)) l = mid;
else r = mid;
}
printf("%.6f\n", r);
return;
}
signed main()
{
//fast;
int T = 1;
//cin >> T;
while(T -- )
solve();
return 0;
}
F - 奶牛晒衣服
思路:
二分答案
- 查找最大的最小值(即,右区间的左端点)
- m i d mid mid 为所用时间,用 r = m i d , l = m i d + 1 r = mid, l = mid + 1 r=mid,l=mid+1 模板
- 若 r e s < = m i d res <= mid res<=mid 即、 m i d mid mid 太小, r = m i d r = mid r=mid;反之, l = m i d + 1 l = mid + 1 l=mid+1
代码如下:
#include <bits/stdc++.h>
#define fast ios::sync_with_stdio(false), cin.tie(nullptr); cout.tie(nullptr)
#define x first
#define y second
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 5e5 + 10, M = 1010, null = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
int n, m1, m2;
int a[N];
bool check(int x)
{
int res = 0;
for (int i = 1; i <= n; i ++ )
{
int j = a[i] - m1 * x;
if(j > 0) res += j / m2 + (j % m2 != 0);
if(res > x) return false;
}
return true;
}
void solve()
{
cin >> n >> m1 >> m2;
for (int i = 1; i <= n; i ++ )
cin >> a[i];
int l = 1, r = 1e9;
while(l < r)
{
int mid = l + r >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
cout << r << endl;
return;
}
signed main()
{
//fast;
int T = 1;
//cin >> T;
while(T -- )
solve();
return 0;
}