Task 3 (p 104)

T1

题目大意:

跳楼: 起跳准备需要花费, 楼之间的高度差也需要花费.
求在给定花费下, 最多可以跳几栋楼

简单分析:

不难发现, 跳楼过程中楼房的高度具有单调性
这样的性质为dp的转移提供了明确的方向

标算:

状态: dp[i][j]表示跳到了第i栋楼,跳了j次的最小花费
转移: 枚举下一次跳到哪个楼上
答案: 枚举所有可能的状态, 将花费满足条件的计入答案即可

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 55;
int n, t, dp[N][N];
//dp[i][j]表示跳到了第i栋楼,跳了j次的最小花费 
struct building {
    int c, h;
}b[N];
bool cmp(building a, building b) {return a.h < b.h;}
int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%d", &b[i].c);
    for(int i = 1; i <= n; ++i) scanf("%d", &b[i].h);
    scanf("%d", &t);

    sort(b+1, b+n+1, cmp);
    memset(dp, 0x3f, sizeof(dp));
    dp[1][1] = b[1].c;
    for(int i = 1; i <= n; ++i) { //现在在第i栋楼上 
        for(int j = 1; j <= n; ++j) { //跳了j次 
            for(int l = i+1; l <= n; ++l) { //下一次跳到第l栋楼上 
                dp[l][j+1] = min(dp[l][j+1], dp[i][j]+b[l].h-b[i].h+b[l].c);
            }
        }
    }
    int ans = 0;
    for(int i = 1; i <= n; ++i) {
        for(int j = 1; j <= n; ++j) {
            if(dp[i][j] <= t) { //统计答案 
                ans = max(ans, j);
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}

T2

题目大意:

有n个数, 给出他们的和(共n*(n-1)/2个)求这n个数, 按字典序输出

简单分析:

另a1,a2,……,an为一组答案,那么给出的n* (n-1)/2个数中,最小的一定等于a1+a2,次小的一定等于a1+a3,为了确定a1 a2 a3我们还需要a2+a3的值。由于a1+a4可能小于a2+a3,所以我们不能确定a2+a3的值,那么由于数据范围挺小的,枚举a2+a3是n* (n-1)/2中的哪一个就好了。确定了a2+a3之后我们发现a1 a2 a3的值就都有了,然后a1+a4一定是剩下中最小的,依次类推一个一个解出剩下的数就搞定了。

标算:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 310;
int n, m, res[N], ans[N][N], a[N*N], cnt;
bool use[N*N];
void check(int x) {
    memset(use, false, sizeof(use));
    if((a[1]+a[2]+a[x])&1) return;
    res[1] = (a[1]+a[2]+a[x])/2 - a[x];
    res[2] = a[1] - res[1];
    res[3] = a[2] - res[1];
    use[1] = use[2] = use[x] = true;//确定前三个 

    for(int i = 4, j = 3; i <= n; ++i) {
        while(j <= m && use[j]) j++;
        if(j > m) return;
        res[i] = a[j] - res[1];
        use[j] = true;
        for(int k = 2; k < i; ++k) {
            if(res[k] > res[i]) return;
            int v = res[k] + res[i];
            int p = lower_bound(a+1, a+m+1, v) - a;
            if(a[p] != v) return;
            int px = p;
            while(px && a[px] == a[p]) px--;
            px++;
            while(px <= m && a[px] == a[p] && use[px]) px++;
            if(a[px] != a[p] || use[px]) return;
            p = px;
            use[p] = true;
        }
    }
    cnt++;
    for(int i = 1; i <= n; ++i) {
        ans[cnt][i] = res[i];
    }
}
int main() {
    scanf("%d", &n);
    m = n*(n-1)/2;
    for(int i = 1; i <= m; ++i) scanf("%d", &a[i]);
    sort(a+1, a+m+1);
    for(int i = 3; i <= m;) { //枚举哪一个是a2+a3 
        check(i);
        int j = i;
        while(j <= m && a[j] == a[i]) j++;
        i = j;
    }
    printf("%d\n", cnt);
    for(int i = 1; i <= cnt; ++i) {
        for(int j = 1; j <= n; ++j) {
            printf("%d", ans[i][j]);
            if(j == n) printf("\n");
            else printf(" ");
        }
    }
    return 0;
} 

T3

不可做的题目大意:

每个街灯上都有一个数,每次询问,第��个街灯到第��个街灯上的数模��等于��的有几个

用命分析:

做法的核心思想是按p的大小分不同的做法,首先注意到p>10000是没有什么意义的,所以我们按照sqrt(10000)=100进行分块来做。

咋都看不懂的标算:

对于p<=100的,我们发现对于p<=100,总共的可能的询问也就只有100*(100-1)/2=4950种(p,v)的取值,所以我们可以预处理这一部分。我们枚举一个p和一个v,对一个(p,v)开一个vector,将所有模p等于v的数全部放到这个vector里面来,那么每次询问的时候,只需要直接在这个vector里面二分区间内有多少个在这个vector里面即可。
对于p>100的,情况就比较多了没法预处理了,但是注意到可能被统计到答案里面的数,只有v,p+v,2p+v…………之类的数,这样可能的数只有sqrt(n)个,所以我们提前对每一个v开一个vector,把所有等于v的数全部放到这个vector里面。之后每一次询问的时候,我们去v,p+v,2p+v……这每一个vector里面二分即可。
这样总的复杂度就是O(nlognsqrt(n))了,就可以过了。vector常数比较大我也不知道能不能过,不过这个也可以直接用数组或者链表实现。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 100009;
const int maxv = 10000;
const int bsz = 100;
const int maxb = 103;

int n, m;
int a[maxn], vb[maxb][maxb], ve[maxb][maxb];
int xb[maxn], xe[maxn];
int i_buf[maxn * maxb * 2], tib;

void pre() {
    memset(ve, 0, sizeof(ve));
    memset(xe, 0, sizeof(xe));
    for (int i = 1; i <= n; ++ i)
        ++ xe[a[i]];
    for (int i = 0; i <= maxv; ++ i) {
        xb[i] = tib;
        tib += xe[i];
        xe[i] = xb[i];
    }
    for (int i = 1; i <= n; ++ i)
        i_buf[xe[a[i]] ++] = i;
    for (int m = 1; m <= bsz; ++ m) {
        for (int i = 1; i <= n; ++ i)
            ++ ve[m][a[i] % m];
        for (int i = 0; i < m; ++ i) {
            vb[m][i] = tib;
            tib += ve[m][i];
            ve[m][i] = vb[m][i];
        }
        for (int i = 1; i <= n; ++ i)
            i_buf[ve[m][a[i] % m] ++] = i;
    }
}

int queryb(int l0, int r0, int p, int k) {
    if (vb[p][k] == ve[p][k])
        return 0;
    int *x1 = lower_bound(i_buf + vb[p][k], i_buf + ve[p][k], l0);
    int *x2 = upper_bound(i_buf + vb[p][k], i_buf + ve[p][k], r0);
    return x2 - x1;
}

int querys(int v, int l0, int r0) {
    if (xb[v] == xe[v])
        return 0;
    int *x1 = lower_bound(i_buf + xb[v], i_buf + xe[v], l0);
    int *x2 = upper_bound(i_buf + xb[v], i_buf + xe[v], r0);
    return x2 - x1;
}

int querya(int l0, int r0, int p, int k) {
    int ans = 0;
    for (int i = k; i <= maxv; i += p)
        ans += querys(i, l0, r0);
    return ans;
}

int main() {
    scanf("%d%d", &n, &m);
    tib = 0;
    for (int i = 1; i <= n; ++ i)
        scanf("%d", a + i);
    pre();
    while (m --) {
        int l, r, p, k;
        scanf("%d%d%d%d", &l, &r, &p, &k);
        if (p <= bsz)
            printf("%d\n", queryb(l, r, p, k));
        else
            printf("%d\n", querya(l, r, p, k));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值