ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2.18题型总结

2022-02-19 08:00:14  阅读:141  来源: 互联网

标签:总结 题型 cnt int max 2.18 tmp dp define


贪心练习题

最大积分

https://www.ybtoj.com.cn/contest/130/problem/6

这题想策略倒不难,价值高的后买,乘以的等级就更高收益更高

难在等级增加时的算法模拟

(PS:题面其实说的很模糊。。T[i]到底表示总购买数还是再购买数也没清楚,导致前半段一直出错)

#define FOR(i,a,b) for(int i = (a);i <= (b);i++)
//
//
    FOR(i,1,n){
        tmp = cnt - t[lev-1];
        cnt = a[i].num+cnt;
        if(cnt < t[lev]){
            ans+=a[i].num*a[i].c*lev;
        }
        else while(1){
            if(cnt < t[lev]){
                ans+=(cnt-t[lev-1])*a[i].c*lev;break;
            }
            else{
                ans+=(t[lev]-t[lev-1]-tmp)*a[i].c*lev;
                lev++;
                tmp = 0;    
            }    
        }
    }

cnt一直表示总购买量,tmp在购买下种物品之前记录,用来补充升级前的那部分

一次升级之后tmp就归0了,等购买下一种再记录。

砍树问题

https://www.ybtoj.com.cn/contest/130/problem/7

这让我想起之前的雷达问题!

同样要找到一个排序方式并说明其依据,那么根据经验,应该按横坐标(x)排序

好处是,只需要依次对相邻两棵树比较,不需要比较不相邻的树

对于P,左边的树到他距离D1,右边D2,那么这棵树的最高允许高度就为h = min(d1,d2),至少要砍去的长度就为max(ai-h,0)。

出栈序列

https://www.ybtoj.com.cn/contest/130/problem/8

字典序对局部最优策略提示很明显了,每次都要尽量找到序列数值最大的数先输出,直到序列全部入栈,再从栈顶依次输出。

我们用O(n)做法,可以记一个数组来找数值最大的数:如果后面的数都比它小,那么他一定要立即输出。

因此建立后缀最大值数组Max[n]。

int main(){
    scanf("%d",&n);
    FOR(i,1,n) scanf("%d",&a[i]);
    Max[n] = a[n];
    ROF(i,n-1,1) Max[i] = max(a[i],Max[i+1]);
    //FOR(i,1,n) printf("%d ",Max[i]); enter;
    //模拟 
    FOR(i,1,n){
        int x = a[i];
        z[++top] = x; //进栈 
        while(z[top] > Max[i+1]) { //如果已是从现在到结束为止最大的数,弹出 
            printf("%d ", z[top--]);
        }
    }
    while(top) printf("%d ",z[top--]); //退完 
}

矩阵问题

 

荷马史诗

https://www.ybtoj.com.cn/contest/130/problem/4

一道构建出模型才能化繁为简的问题。

题的含义是建立n个k进制数,使他们没有前缀包含另一个数

建立k进制数好办,保证条件就有难度了。

题解给的算法中运用了树:

 

 发现,如果将根到某个叶子路径边权按顺序连就得到了一个Si,一个叶子对应一个Si,同时可以满足前面的前缀条件!

那么,问题就转化为建造一个k叉树。

再想想最优方案:给边加上边权(Si出现的次数为w[i],对应叶子节点的深度为dep[i](也可以说单词i转化为的字符个数),那么总长为

所有n个词的dep[i]*w[i]

那么,边权较小的叶子节点应该深度较浅,以达到整体总长更小

比如上图,原来3位置没有节点,那么将2节点挪移过去,一定不会比挪移前更差。

但是要只比较边权较小的叶子节点也不行,建不起多层树,所以我们用a[i]表示i子树中边权和,按a[i]从小到大取。

在计算中,不能保证每个树都有k个节点,计算的时候各种空位麻烦,所以我们搬出优化策略:

优化策略:补充若干个w=0的节点,使(n-1)%(k-1)=0

这样可以每次取前k个,这个算法就完美了

附代码

#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i = (a);i <= (b);i++)
#define int long long
#define il inline 
typedef long long ll;
using namespace std;
const int M = 110086;
int n,k,cnt,ans,w[M];
struct tle{
    int v; //该点权值 
    int h; //该点子树高度 
};
il bool operator < (const tle &a,const tle &b){
    if(a.v!=b.v) return a.v > b.v; 
    else return a.h > b.h;
}
priority_queue <tle>q;
signed main(){
    cin>>n>>k;
    FOR(i,1,n) {
        scanf("%lld",&w[i]);
        tle tmp;
        tmp.v = (ll)w[i];
        tmp.h = 0;
        q.push(tmp);
    } //初始化:全是叶子,深度为0,权值w[i]
    cnt = n;
    if((n-1)%(k-1)!=0){
        cnt+=k-1-(n-1)%(k-1);
    } 
    FOR(i,1,cnt-n) {
        tle tmp;
        tmp.v = 0ll;
        tmp.h = 0;
        q.push(tmp);
    } //n-1不是k-1整数倍时,用权值为0的点补全
    while(cnt != 1) {
        ll val = 0ll;
        int dep = 0;
        FOR(i,1,k){ //step1:取出前k小 
            tle tot = q.top();
            val += (ll) tot.v;
            dep = max(dep,tot.h);
            q.pop();
        }
        cnt = cnt - k + 1; //step2:cnt减值
        ans += val;//step3:加上权值之和 
        tle tmp;
        tmp.v = val;
        tmp.h = dep+1; //+1是因为要变父亲了,加辈
        q.push(tmp);  //step4:把父亲压进去,合并完成,一次循环结束 
    }
    cout << ans << endl << q.top().h << endl;
    return 0;
}

二分练习题

 

 二分的适用范围:最优解具有单调性

合法|不合法:把最优化问题转化为判定问题

数列分段

https://www.ybtoj.com.cn/contest/131/problem/1

经典的二分转化--找到合适的L与R(此题是数列max到数列sum)--二分判断mid是否满足--满足则大于mid部分丢掉,不满足则小于mid部分丢掉

#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i = (a);i <= (b);i++)
#define mid ((l+r)>>1)
using namespace std;
int n,m,a[100005],max_of_ai,sum_of_ai;
int ck(int limit){
    int cnt = 1,sum = 0;
    FOR(i,1,n) {
        if(sum + a[i] <= limit) sum+=a[i];
        else cnt++,sum = a[i];
    }//单调选择,看作找不大于limit的最少连续段的问题 
    return cnt <= m;
}
int main(){
    scanf("%d%d",&n,&m);
    FOR(i,1,n) scanf("%d",&a[i]),max_of_ai = max(max_of_ai,a[i]),sum_of_ai += a[i];
    int l = max_of_ai,r = sum_of_ai;
    while(l < r){
        //printf("%d %d\n",l,r);
        if(ck(mid)) r = mid;
        else l = mid+1;
    }
    cout<<l<<endl;
}

攻击法坛

https://www.ybtoj.com.cn/contest/131/problem/6

二分的题各不相同,但是能推出二分的模型,按照题本身特点来做

这个题特点是融合DP

#include<bits/stdc++.h>
#define define define 
#define enter puts("\n")
#define FOR(i,a,b) for(int i = (a);i <= (b);i++)
#define mid (l+r)/2
using namespace std;
const int MAXN = 2005;
int n, R, G, a[MAXN], l = 1, r = 0,ans = 0,cnt;
int dp[MAXN][MAXN],p[MAXN],q[MAXN];
bool ck (int L){
    memset(dp,0,sizeof(dp));
    memset(p,0,sizeof(p));
    memset(q,0,sizeof(q));
    FOR(i,1,n) FOR(j,i,n){
        if(a[j]-a[i]+1<=L) p[i] = j;if(a[j]-a[i]+1<=2*L) q[i] = j;
    }
    p[n+1] = q[n+1] = n;
    FOR(i,0,R) FOR(j,0,G){
        cnt++;
        if(i>0) dp[i][j]=max(dp[i][j],p[dp[i-1][j]+1]);
        if(j>0) dp[i][j]=max(dp[i][j],q[dp[i][j-1]+1]);
    }//枚举 
    return dp[R][G]==n;
}
int main(){
    scanf("%d%d%d",&n,&R,&G);
    FOR(i,1,n) scanf("%d",&a[i]);
    sort(a+1,a+n+1);
    int l = 1,r = a[n] - a[1] + 1;a[0] = 0;
    if(R+G >= n) return printf("1\n"),0;
    while(l <= r){
        if(ck(mid)) ans = mid,r = mid-1;else l = mid+1;
    }
    cout<<ans<<endl;
}

 

标签:总结,题型,cnt,int,max,2.18,tmp,dp,define
来源: https://www.cnblogs.com/y1nfelix/p/15907675.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有