ACM-9月24日周日周末训练心得

本文回顾了两场网络赛的经历,详细介绍了比赛中遇到的问题及解决方案,包括使用循环遍历求解最小排队人数、线段树和树状数组的应用,以及尝试解决动态规划问题等。

    这周又是两场网络赛,周六的北京赛区实在是战况惨烈,只出了第一道题,签了个到,第一题是我写的= =,队友们全部卡在后面的题目了,第一题大致的意思就是小明旅游n天有m天呆在北京,m天中的第一天去北大,剩下的天里再找一天去北大,然而每天在北大排队的人数不同,求总排队人数最少的天,而且有些天是交通管制哪里都不能去,所以要花k天在北京减去交通管制的天数正好是m天,我的做法就是用循环去遍历,交通管制的那天我自动将排队人数变成-1处理。

 

#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
    int n,m,q,r,min,resr,resl,cnt;
    int p[100];
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        min=2001;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&p[i]);
        }
        scanf("%d",&q);
        for(int i=0;i<q;i++)
        {
            scanf("%d",&r);
            p[r]=-1;
        }
        for(int k=m;k<=n;k++)
        {
            for(int i=0;i<=n-k;i++)
            {
                cnt=0;
                if(p[i]==-1)continue;
                for(int j=i+1;j<=i+k-1;j++)
                {
                    if(p[j]==-1)cnt++;
                }
                if(k-cnt==m)
                {
                   for(int j=i+1;j<=i+k-1;j++)
                    {
                        if(p[i]+p[j]<=min&&p[j]!=-1){min=p[i]+p[j];resr=i;resl=j;}
                    }
                }
                else continue;
            }
        }
        printf("%d %d\n",resr,resl);
    }
}

    然后今天的南宁赛区的网络赛,A了两道题,怎么说呢,线段树和树状数组如果再看的多一点的话可能能写更多,在此,谴责自己。

 

    A的第一道题是B,这道题算的是列车上最大的乘客位数,一开始用的线段树,由于对更新线段树的掌握还不是很透彻,发现把问题想复杂了,最后改为暴力就过了

 

#include<string.h>
#include<math.h>
#include<vector>
#include<cstdio>
#include<cstring>
using namespace std;
struct node
{
    int x;
    int val;
    int cnt;
}a[2005];
bool cmp(node qq,node ww)
{
    if(qq.x==ww.x)
    {
        return qq.cnt>ww.cnt;
    }
    return qq.x<ww.x;
}
int main()
{
    int n;
    while(scanf("%d",&n))
    {
        if(n==0) {cout<<"*"<<endl;break;}
        int o=0;
        for(int i=1;i<=n;i++)
        {
            int h,h1,h2;
            scanf("%d%d%d",&h1,&h2,&h);
            if(h1>h2) swap(h1,h2);
            if(h1==h2) continue;
            a[++o].x=h1;
            a[o].val=h;
            a[o].cnt=1;

            a[++o].x=h2-1;
            a[o].val=h;
            a[o].cnt=-1;
        }
        sort(a+1,a+o+1,cmp);
        int sum=0,ans=0;
        for(int i=1;i<=o;i++)
        {
            if(a[i].cnt==1) sum+=a[i].val;
            if(a[i].cnt==-1) sum-=a[i].val;
            if(sum>ans) ans=sum;
        }
        cout<<ans<<endl;
    }
}

    第二个是完全套用的线段树扫描的模板,极其类似,然后就过了。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=10005;
#define lson i*2,l,m
#define rson i*2+1,m+1,r
#define root 1,1,k-1
int X[MAXN];
struct node
{
    int l,r,h;
    int d;
    node(){}
    node(int a,int b,int c,int d): l(a),r(b),h(c),d(d){}
    bool operator <(const node &b)const
    {
        return h<b.h;
    }
}nodes[MAXN];
int cnt[MAXN*4];
int sum[MAXN*4];
void PushDown(int i,int l,int r)
{
    int m=(l+r)/2;
    if(cnt[i]!=-1)
    {
        cnt[i*2]=cnt[i*2+1]=cnt[i];
        sum[i*2]= (cnt[i]?(X[m+1]-X[l]):0) ;
        sum[i*2+1]= (cnt[i]?(X[r+1]-X[m+1]):0) ;
    }
}
void PushUp(int i,int l,int r)
{
    if(cnt[i*2]==-1 || cnt[i*2+1]==-1)
        cnt[i]=-1;
    else if(cnt[i*2]!= cnt[i*2+1])
        cnt[i]=-1;
    else
        cnt[i]=cnt[i*2];
    sum[i]=sum[i*2]+sum[i*2+1];
}
void build(int i,int l,int r)
{
    if(l==r)
    {
        cnt[i]=0;
        sum[i]=0;
        return ;
    }
    int m=(l+r)/2;
    build(lson);
    build(rson);
    PushUp(i,l,r);
}
void update(int ql,int qr,int v,int i,int l,int r)
{
    if(ql<=l && r<=qr)
    {
        if(cnt[i]!=-1)
        {
            cnt[i]+=v;
            sum[i] = (cnt[i]? (X[r+1]-X[l]):0);
            return ;
        }
    }
    PushDown(i,l,r);
    int m=(l+r)/2;
    if(ql<=m) update(ql,qr,v,lson);
    if(m<qr) update(ql,qr,v,rson);
    PushUp(i,l,r);
}
int bin(int key,int n,int d[])
{
    int l=1,r=n;
    while(r>=l)
    {
        int m=(r+l)/2;
        if(d[m]==key)
            return m;
        else if(d[m]>key)
            r=m-1;
        else
            l=m+1;
    }
    return -1;
}
int main()
{
    int q;
    int kase=0;
    while(scanf("%d",&q))
    {
        if(q==0){cout<<"*"<<endl;break;}
        int n=0,m=0;
        for(int i=1;i<=q;i++)
        {
            int x1,y1,x2,y2;
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            X[++n]=x1;
            nodes[++m]=node(x1,x2,y1,1);
            X[++n]=x2;
            nodes[++m]=node(x1,x2,y2,-1);
        }
        sort(X+1,X+n+1);
        sort(nodes+1,nodes+m+1);
        int k=1;//共k个不同的x坐标,组成了k-1个不同的区域
        for(int i=2;i<=n;i++)
            if(X[i]!=X[i-1]) X[++k]=X[i];
        build(1,1,k-1);//少了build就WA
        int ret=0;//最终面积
        for(int i=1;i<m;i++)
        {
            int l=bin(nodes[i].l,k,X);
            int r=bin(nodes[i].r,k,X)-1;
            if(l<=r) update(l,r,nodes[i].d,root);
            ret += sum[1]*(nodes[i+1].h-nodes[i].h);
        }
        cout<<ret<<endl;
    }
}

    比较可惜的是第L题,用的动态规划,求最重的上升子序列,最后是以内存超了为结束,想不到如何解决。

 

 

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
#include<cmath>
#include<vector>
using namespace std;
int main()
{
    int dp[200001],c,maxw,q=0;
    char l;
    vector<int> a,w;
    while(scanf("%d",&c))
    {
        if(c<0)
        {
            a.push_back(c);
            w.push_back(0);
        }
        else if(c>=10000)
        {
            a.push_back(c-10000);
            w.push_back(5);
        }
        else
        {
            a.push_back(c);
            w.push_back(1);
        }
        l=getchar();
        if(l=='\n')break;
    }
    int n=a.size();
    //cout<<n<<endl<<endl;
    dp[0]=w[0];//表示以w[0]为子序列最右边的重量
    for (int i=1;i<n;i++)
    {
        dp[i]=w[i];
        for (int j=0;j<i;j++)
        {
            if (a[i]>=a[j]&&dp[j]+w[i]>dp[i])
                dp[i]=dp[j]+w[i];
        }
    }
    for(int i=maxw=0;i<n;i++)
    {
        maxw=max(maxw,dp[i]);
        //cout<<dp[i]<<endl;
    }
    cout<<maxw<<endl;
}

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值