一些sb题(day1下)

t1
有两副牌,每副牌都有n张。
对于第一副牌的每张牌长和宽分别是xi和yi。对于第二副牌的每张牌长和宽分别是aj和bj。第一副牌的第i张牌能覆盖第二副牌的第j张牌当且仅当xi>=aj并且yi>=bj。(注意牌不能翻转)当然一张牌只能去覆盖最多一张牌,而不能覆盖好多张。
LYK想让两副牌的各n张一一对应叠起来。它想知道第二副牌最多有几张能被第一副牌所覆盖。

这个题用贪心
对两副牌各自进行排序,然后贪心
枚举a,找a能覆盖的所有牌,去盖最接近的,为什么这样行呢
你的x是从小到大的,后面的牌只可能盖不到y,x一定能盖到
我是对y贪的,不对,因为不是所有牌都要盖。。
然后某些东西用平衡树维护。。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <set>
using namespace std;
int n;
multiset <int> s;
struct node {int x,y;} a[100005],b[100005];
int cmp(node i,node j) {return i.x<j.x;}
int main()
{
    freopen("water.in","r",stdin);
    freopen("water.out","w",stdout);
    int T;
    T=1;
    while(T--)
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++) scanf("%d%d",&a[i].x,&a[i].y);
        for(int i=0;i<n;i++) scanf("%d%d",&b[i].x,&b[i].y);
        sort(a,a+n,cmp);
        sort(b,b+n,cmp);
        s.clear();
        int k=0,ans=0;
        for(int i=0;i<n;i++)
        {
            while(a[i].x>=b[k].x&&k<n)
            {
                 s.insert(b[k].y);
                 k++;
            }
            if(s.empty())continue;
            multiset<int>::iterator it=s.upper_bound(a[i].y);
            if (it==s.begin()) continue; it--;
            ans++; s.erase(it);
        }
        printf("%d\n",ans);
    }
    return 0;
}

t2
题意
求最少有多少数能组成1~n的所有数
这个最少的就是用二进制组的,1 2 4 8一定能
方案就用dp
dp[i][j][k] 当前有i个金币,金币和是j,下一个金币。
第一层可以滚动掉

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
int opt,n,ans,dp[2][1100][1100];
int main(){
    scanf("%d",&n);
    int s=log(n)/log(2)+1;
    printf("%d ",s);
    dp[0][1][1]=1;
    for(int i=1;i<s;i++) {
        memset(dp[opt^1],0,sizeof dp[opt^1]);
        for(int j=1;j<=n;j++)
        for(int k=1;k<=n;k++)
        if(dp[opt][j][k])
        for(int q=k+1;q<=j+1;q++)
        dp[opt^1][std::min(n,j+q)][q]+=dp[opt][j][k];
        opt^=1;
    }
    for(int i=1;i<=n;i++) ans+=dp[opt][n][i];
    printf("%d",ans);
} 

t3
60
简单的动规。。

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int dp[1100][21][1100],n,k,a[110000],s[1100][1100],ans=199999999;
int main() {
    freopen("dp.in","r",stdin);
    freopen("dp.out","w",stdout);
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) {
        scanf("%d",&a[i]);
        for(int j=1;j<=n;j++) s[i][j]=s[i-1][j];
        s[i][a[i]]++; 
    }
    memset(dp,127,sizeof dp);
    dp[1][1][1]=0;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=k;j++){
        if(j<k){
            for(int q=j-1;q<i;q++)  
        dp[i][j][q]=min(dp[i-1][j][q]+s[i-1][a[i]]-s[q-1][a[i]],dp[i][j][q]);
        for(int q=j-1;q<i;q++)
        dp[i][j+1][i]=min(dp[i-1][j][q],dp[i][j+1][i]);
        }
        else {
            for(int q=j-1;q<i;q++)
            dp[i][j][q]=min(dp[i-1][j][q]+s[i-1][a[i]]-s[q-1][a[i]],dp[i][j][q]);
        }

    }

    for(int i=k;i<n;i++) 
    ans=min(ans,dp[n][k][i]);
    printf("%d ",ans);
}
正解`不会
#include<iostream>
#include<cstdio>
using namespace std;
const int N=100010;
typedef long long LL;
int c[N],a[N];
LL f[N],g[N];
int p,q,n,k;
LL tot;
void move(int l,int r)  // [p,q]之前的区间
{
    while (l<p) p--,tot+=c[a[p]],c[a[p]]++;
    while (r>q) q++,tot+=c[a[q]],c[a[q]]++;
    while (p<l) c[a[p]]--,tot-=c[a[p]],p++;
    while (r<q) c[a[q]]--,tot-=c[a[q]],q--;
}
void work(int l,int r,int fl,int fr)
//需要求dp[fl] ~ dp[fr]  最优解一定从l~r中的某一个转移过来
{
    if (fl>fr) return;
    int mid=(fl+fr)>>1,mi;
    LL mx=1LL<<60;
    for (int i=l;i<=r;i++)
    if (i<mid)
    {
        move(i+1,mid); -> tot=sum(i+1,mid);
        if (f[i]+tot<mx) mx=f[i]+tot,mi=i;
    }
    g[mid]=mx;
    work(l,mi,fl,mid-1);
    work(mi,r,mid+1,fr);
}
int main()
{
    freopen("dp.in","r",stdin);
    freopen("dp.out","w",stdout);
    scanf("%d%d",&n,&k);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    f[0]=0;
    for (int i=1;i<=n;i++) f[i]=1LL<<60;
    while (k--)
    {
        p=1,q=0,tot=0;
        for (int i=1;i<=n;i++) c[i]=0;
        work(0,n-1,1,n);
        for (int i=0;i<=n;i++) f[i]=g[i],g[i]=0;
    }
    cout<<f[n];
    return 0;
}`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值