寒假训练——第一周(二分)

A - 栅栏

A - 栅栏

思路:

二分 + D F S DFS DFS + 剪枝

总体思路如下:

  1. m i d mid mid 为最多可以剪出的木材数(升序排列)
  2. 按照每一个 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=mid1

剪枝:

  1. 优化搜索顺序:木材( s m a l l small small) 从从大到小枚举,木板( b i g big big ) 从小到大枚举,产生的分支数量较少
  2. 可行性剪枝 :( 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] )
  3. 可行性剪枝:若 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]
  4. 排除等效冗余: s m a l l [ k ] small[k] small[k] = s a m l l [ k − 1 ] samll[k-1] samll[k1],继续从当前位置开始枚举

代码如下:

#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 - 扑克牌

B - 扑克牌

思路:

二分答案

  1. m i d mid mid 为所求扑克牌的套数
  2. 可用的 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 的数目
  3. 若没超出 l = m i d l = mid l=mid ;反之, r = m i d − 1 r = mid - 1 r=mid1

代码如下:

#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 - 跳石头

C - 跳石头

思路:

二分答案

  1. m i d mid mid 为跳跃距离的最大值
  2. 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++
  3. 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 - 小车问题

D - 小车问题

思路:

浮点数二分答案:

  1. 甲的总时间为:车 t i m e time time + 人 t i m e time time;乙的总时间为:人 t i m e time time + 车 t i m e time time,若时间最短,则甲乙同时到终点
  2. m i d mid mid 表示 甲坐车走的距离,根据距离算甲乙的时间
  3. 若 甲 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 - 切绳子

E - 切绳子

思路:

浮点数二分答案:

  1. 查找最小长度的最大值(左半区间的右端点)
  2. m i d mid mid 表示为绳子的长度
  3. 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
  4. 反之, 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 - 奶牛晒衣服

F - 奶牛晒衣服

思路:

二分答案

  1. 查找最大的最小值(即,右区间的左端点)
  2. m i d mid mid 为所用时间,用 r = m i d , l = m i d + 1 r = mid, l = mid + 1 r=mid,l=mid+1 模板
  3. 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AC自动寄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值