• 20201026day46 刷题记录


    1 游戏通关

    problem

    小明需要完成(N)个任务才能将这个游戏通关。
    每个任务完成时限(T),就是这个任务必须在时间(T)之前完成(你可以认为游戏刚开始的时间为1),还有完成这个任务小明可以获得一定的奖励(W)。由于小明娴熟的技术以及任务的简单,他可以在一个单位时间将任务完成。
    小明想要在老师到来之前将任务全部完成,同时他也想获得最多的奖励。
    (N≤2 imes 10^5,T_i≤2 imes 10^5,W_i≤2 imes 10^3)

    solution

    这道题和智力大冲浪相同,只是将最小减少奖励改为最大增加奖励;
    奖励从大到小排序,并且在可行区间内尽量往后放。
    不过数据范围较大,如果暴力枚举1~t[i]大概会超时。
    优先队列优化。

    code

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    using namespace std;
    long long read(){
    	long long x=1,a=0;char ch=getchar();
    	while (ch<'0'||ch>'9'){if (ch=='-') x=-1;ch=getchar();}
    	while (ch>='0'&&ch<='9'){a=a*10+ch-'0';ch=getchar();}
    	return x*a;
    }
    #include<queue>
    const int maxn = 200005;
    struct node{
        int t,v;
    }a[maxn];
    priority_queue<int>q;
    bool cmp(const node x,const node y){
        return x.t > y.t;
    }
    int n,ttt,ans,js;
    int main(){
        n=read();
        for(int i = 1;i <= n;i++)
        	a[i].t=read(),a[i].v=read(),ttt=max(a[i].t,ttt);
        sort(a+1,a+n+1,cmp);
        for(int i = ttt;i >= 1;i --)
        {
            while(a[++ js].t >= i)q.push(a[js].v);
            js --;
            if(!q.empty())
            {
                ans += q.top();
                q.pop();
            }
        }
       printf("%d",ans);
        return 0;
    }
    

    2 优惠券

    problem

    新学期开学了,Smart准备买一些新书,书店有(N)本新书,第(i)本新书价格为(P_i),使用优惠券购买第(i)本书时价格会降为(C_i)。Smart有(K)张优惠券,每本新书只能使用一次优惠券。Smart想知道花不超过M的钱最多可以买多少本新书?

    solution

    要买的最多,那一定是先买优惠券价最便宜的。
    如果优惠券没用完,钱就花光了,当前买的书即为答案。
    如果钱还有剩,那么对于剩下的n-k本书,判断是用原价买它,或把另一本书的优惠券给它而另一本原件买比较便宜。
    但是若枚举所有已经使用优惠券的书,时间复杂度显然不友好。
    交换条件为c[j]+p[i] < c[i]+p[j],即p[i]-c[i] < p[j]-c[j]
    因此,优先队列要维护的是:原价和优惠券价的差值,从小到大排。每当对某本书使用优惠券时,把它的差值压入优先队列。

    code

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    #include <queue>
    using namespace std;
    long long read(){
    	long long x=1,a=0;char ch=getchar();
    	while (ch<'0'||ch>'9'){if (ch=='-') x=-1;ch=getchar();}
    	while (ch>='0'&&ch<='9'){a=a*10+ch-'0';ch=getchar();}
    	return x*a;
    }
    const int maxn=5e4+10;
    struct node{
    	int p,c;	
    }a[maxn];
    long long n,k,m;
    bool cmp(node x,node y){return x.c<y.c;}
    priority_queue<int,vector<int>,greater<int> > q1;
    priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q2,q3;
    int book[maxn];
    int main(){
    	n=read(),k=read(),m=read();
    	for(int i=1;i<=n;i++) a[i].p=read(),a[i].c=read();
    	sort(a+1,a+n+1,cmp);
    	long long now=0;	
    	for(int i=1;i<=k;i++){
    		now+=a[i].c;
    		if(now>m) {printf("%d
    ",i-1);return 0;}	
    		q1.push(a[i].p-a[i].c);
    	}
    	if(k==n){printf("%d
    ",n);return 0;}
    	for(int i=k+1;i<=n;i++){
    		q2.push(make_pair(a[i].c,i));
    		q3.push(make_pair(a[i].p,i));	
    	}
    	int ans=k;
    	for(int i=k+1;i<=n;i++){
    		while(book[q2.top().second]) q2.pop();
    		while(book[q3.top().second]) q3.pop();
    		int p1=q2.top().second;
    		int p2=q3.top().second;
    		int tmp1=q2.top().first+q1.top();
    		int tmp2=q3.top().first;
    		if(tmp1<tmp2){
    			now+=tmp1;q1.pop();q2.pop();q1.push(a[p1].p-a[p1].c);
    			book[p1]=1;	
    		}
    		else{
    			now+=tmp2;
    			q3.pop();book[p2]=1;	
    		}
    		ans++;
    		if(now>m){printf("%d
    ",ans-1);return 0;}
    	}
    	printf("%d
    ",n);
    	return 0;
    }
    

    3 探讨人生

    problem

    Smart每次他与好友A探讨人生要花费(a)个小时,并可以得到(x)点人生经验;每次与好友(B)探讨人生要花费(b)个小时,并得到(y)点人生经验。但是Smart的精力是有限的,他只能抽出(n)个小时来跟他的友人们探讨人生,若这(n)个小时并没有被用完,则Smart会把剩下的时间拿来跟好友C聊天,而这并不能得到人生经验。现在Smart想知道,他最多可以得到多少点人生经验。
    【没数据范围】

    solution

    因为只有两个人,所以暴力枚举探讨次数就可以了。
    剪枝:先swap一下,保证与A探讨单位时间经验更多。
    为了使枚举的探讨次数控制在(sqrt{n})以内,需要判断一下:若(a>sqrt{n}),则枚举次数一定小于(sqrt{n}),正常枚举与(a)探讨次数即可。否则,枚举与B探讨次数。
    因为与A探讨更优,即与A探讨(b)次优于与B探讨(a)次,所以当枚举与B探讨次数大于等于(a)时,一定不如把其中的(a)次变成和A探讨。
    所以最多只要枚举(a)次。

    code

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    
    #define ll long long
    
    using namespace std;
    
    ll read() {
        ll a = 0, x = 1;
        char ch = getchar();
        while (ch > '9' || ch < '0') {
            if (ch == '-')
                x = -1;
            ch = getchar();
        }
        while (ch >= '0' && ch <= '9') {
            a = a * 10 + ch - '0';
            ch = getchar();
        }
        return a * x;
    }
    
    ll n, x, y, a, b, maxn = 0;
    
    int main() {
        n = read(), x = read(), y = read(), a = read(), b = read();
        if (x * 1.0 / a < y * 1.0 / b) {
            swap(a, b);
            swap(x, y);
        }
        if (a >= sqrt(n)) {
            for (int i = 0; i * a <= n; i++) {
                maxn = max(maxn, i * x + (n - i * a) / b * y);
            }
        } else {
            for (int i = 0; i <= a && i * b <= n; i++) {
                maxn = max(maxn, i * y + (n - i * b) / a * x);
            }
        }
        printf("%lld", maxn);
        return 0;
    }
    

    4 数字游戏

    problem

    小W发明了一个游戏,他在黑板上写出一行数字(a_1,a_2,…a_n(n≤200)),然后给你(m)个回合的机会,每个回合你可以从中选一个数擦除它,接着剩下来的每个数字(a_i)都要递减一个值(b_i)。如此重复(m)个回合,所有你擦除的数字之和就是你得到的分数。
    编程帮小W算算,对于每个给出的(a_n)(b_n)序列,可以得到的最大得分是多少?

    solution

    贪心得到dp枚举顺序。
    当m=n,即全选时,显然按b从大到小选。
    m<n时,需要用dp解决,但策略是相同的,即把b从大到小排序。
    设f[j]表示选j个数得到的最大价值。
    状态转移方程:f[j] = max(f[j-1]+a[i]-b[i]*(j-1))

    code

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    #include <queue>
    using namespace std;
    long long read(){
    	long long x=1,a=0;char ch=getchar();
    	while (ch<'0'||ch>'9'){if (ch=='-') x=-1;ch=getchar();}
    	while (ch>='0'&&ch<='9'){a=a*10+ch-'0';ch=getchar();}
    	return x*a;
    }
    const int maxn=201;
    struct node{
    	int a,b;	
    }num[maxn];
    int n,m;
    int f[2][N];//f[i][j]表示前i个数中取了j个数的最优解
    //f[i][j]=max(f[i-1][j],f[i-1][j-1]+(a[i]-b[i])*(j-1)) 
    void init(){
    	n=read(),m=read();
    	for(int i=1;i<=n;i++) num[i].a=read();
    	for(int i=1;i<=n;i++) num[i].b=read();	
    }
    bool cmp(node p,node q){return p.b>q.b;	}
    void work(){
    	sort(num+1,num+1+n,cmp);
    	memset(f,0,sizeof(f));
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=min(i,m);j++)
    			f[i%2][j]=max(f[(i+1)%2][j],f[(i+1)%2][j-1]+num[i].a-num[i].b*(j-1));
    	printf("%d
    ",f[n%2][m]);	
    }
    int main(){
    	init();work();return 0;	
    }
    
  • 相关阅读:
    还算满意的流程图
    C#WinCE程序(.NET Compact Framework 3.5)项目重构面向抽象设计
    c#使用PortableDeviceApiLib读取便携式设备(WPD:Windows Portable Devices)信息
    缅怀逝去的青葱岁月, 忆“煤油灯”
    又趟一个坑,IO卡信号DI和DO的信号类型
    C#Winform使用mysql作为本地数据库
    C#winform自定义控件模拟设计时界面鼠标移动和调节大小、选中效果
    zxing .net 多种条码格式的生成
    C#单例测试(懒汉式双锁保证线程安全)
    架构师之路->架构师思维的培养
  • 原文地址:https://www.cnblogs.com/liuziwen0224/p/20201026day26-001.html
Copyright © 2020-2023  润新知