链接:https://vjudge.net/contest/401586#problem/C
题意:
有n个骆驼过桥,骆驼的重量为w1,w2,...,wn,桥由M段连成,每段的长度为li,承重为wi,可以调整骆驼的先后顺序,问骆驼能通过桥首尾的最短距离为多少?
思路:
n很小,那么可以暴力出n的全排列。
令dp[i]表示从第1只骆驼到第n只骆驼能通过的最短距离。
转移方程:
dp[i]=max(dp[i],dp[j] + len(j, i)){len(j,i)为j到i的最短距离}
解释一下转移方程:
dp[j]是前j个能通过,那么不管它们,考虑j到i能通过的j到i的最短距离?
如何判断能通过的最短距离?那么就是从桥中找到最短的一节桥,且使得桥的承重大于等于j到i的总重量。这个可以通过二分去找。(二分要预处理桥的长度哟,这是因为选中一节桥作为距离,大于这个长度的在过桥中也要满足)dp[i]可以通过1到i-1的所有情况转移过来,实际上这些情况在过桥的过程中都会发生(或者说1-i的过桥都可以由这个转移方程来理解),因此要取max。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
template<class T>
void read(T &res) {
    res = 0;T f = 1;char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = getchar();
    }
    res *= f;
}
const int maxn = 1e5+7;
struct Node{
    int l, v;
    bool operator < (const Node & n1) const{
        if(l == n1.l) {
            return v > n1.v;
        }
        else return l < n1.l;
    }
}a[maxn];
int n, m, res, b[10], w[10], s[10], dp[10];
bool vis[10];

//int bsearch(int x)
//{
//    int l = 1, r = m;
//    int res = 0;
//    while (l < r)
//    {
//        int mid = l + r  >> 1;
//        if (x > a[mid].v) {
//            res = a[mid].l;
//            l = mid;
//        }
//        else r = mid - 1;
//    }
//    return res;
//}


int find(int x) {
    int l = 1, r = m;
    int res = 0;
    while(l <= r) {
        int mid = l + r + 1>> 1;
        if(x > a[mid].v) {
            res = a[mid].l;
            l = mid + 1;
        } else r = mid - 1;
    }
    return res;
}

//dp[i] = max(dp[i], dp[j] + len)
void work()
{
    memset(dp, 0, sizeof(dp));
    for(int i = 1; i <= n; i++) {
        s[i] = s[i-1] + w[b[i]];
    }
    for(int i = 2; i <= n; i++) {
        for(int j = 1; j <= i-1; j++) {
            int tmp = s[i] - s[j-1];
            int len = bsearch(tmp);
            dp[i] = max(dp[i], dp[j] + len);
        }
    }
    res = min(res, dp[n]);
}

void dfs(int cnt) {
    if(cnt == n + 1) {
        work(); //这个work函数好方便,学到了 
        return ;
    }
    for(int i = 1; i <= n; i++) {
        if(vis[i]) continue;
        vis[i] = 1;
        b[cnt] = i;
        dfs(cnt + 1);
        vis[i] = 0;
    }
}

signed main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++) read(w[i]);
    for(int i = 1; i <= m; i++) {
        read(a[i].l); read(a[i].v);
    }
    sort(a + 1, a + m + 1);
    int mi = INF;
    for(int i = m; i >= 1; i--) {
        mi = min(mi, a[i].v);
        a[i].v = mi;
    }
    for(int i = 1; i <= n; i++) {
        if(w[i] > mi) {
            cout << -1 << endl;
            return 0;
        }
    }
    res = INF;
    dfs(1);
    cout << res << '\n';
}