2013暑假集训第一天总结。

今天做了两场比赛,上午一场下午一场,

上午做的这一场 http://acm.hust.edu.cn/vjudge/contest/view.action?cid=25949#overview  密码是oucouc   OUC_Summer Training_DIV2_ #1

下午做的这一场 http://acm.hust.edu.cn/vjudge/contest/view.action?cid=26099#overview  同上, OUC_Summer Training_ DIV2_#2之解题策略

写一下总结

上午这一场,最后三个题很好,

A 水题

B 水题

C  给定一些时间的开始时间和结束时间,问有多少事件被其他事件完全覆盖

  有一个很重要的条件就是各个事件的开始结束时间都是互不相同的。所以排序后直接贪心就行了:

#define maxn 100005
struct node
{
    int s,t;
};
node a[maxn];
int n ;
bool cmp(node t1,node t2)
{
    if(t1.s != t2.s ) return t1.s > t2.s;
    return t1.t < t2.t;
}
int num[maxn];
int id ;
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i = 0 ; i< n ; i ++ )
        {
            scanf("%d%d",&a[i].s,&a[i].t);
        }
        sort(a , a + n , cmp);
    /*
printf("
");
        for(int i = 0 ; i< n ; i++ )
            printf("%d %d
",a[i].s,a[i].t);
*/
        int ans = 0 ;
        int th ;
        th = a[n-1].t;
        for(int i = n - 2; i >= 0 ;i -- )
        {
            if(a[i].t > th )
            {
                th = a[i].t;
            }
            else
                ans ++ ;
        }
        printf("%d
",ans);
    }
    return 0;
}

D题:抽象一下就是问 a,b,a,b,a,b,这样的的序列最长有多长。codeforces 256A,算的上是个好题,

暴力写挫了,不该用vector。

#define maxn 5005
struct node
{
    int x,id;
};
node a[maxn];
vector<int> b[maxn];
int val[maxn];
int num ;
bool cmp(node t1,node t2)
{
    if(t1.x != t2.x ) return t1.x < t2.x;
    return t1.id < t2.id;
}
int merge(vector<int> t1 , vector<int> t2)
{
    int n1 = t1.size();
    int n2 = t2.size();
    int ret ;
    ret = 1 ;
    int _min , _w;
    int i ,j;
    i = 0 ;
    j = 0 ;
    _min = t1[0] < t2[0] ? t1[0] : t2[0] ;
    _w = t1[0] < t2[0] ? 0 : 1 ;
    if(t1[0] == _min )
        i ++ ;
    else j ++ ;
    while(i < n1 && j < n2 )
    {
        if(t1[i] < t2[j] )
        {
            i ++ ;
            if(_w == 1 )
            {
                _w = 0 ;
                ret ++ ;
            }
        }
        else
        {
            j ++ ;
            if(_w == 0 )
            {
                _w = 1 ;
                ret ++ ;
            }
        }
    }
    if(i < n1 )
    {
        if(_w == 1 )
        {
            ret ++ ;
        }
    }
    if(j < n2 )
    {
        if(_w == 0 )
        {
            ret ++ ;
        }
    }
    return ret;
}
int main()
{
    int n ;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i = 0 ; i < n ; i++ )
        {
            scanf("%d",&a[i].x);
            a[i].id = i ;
        }
        sort(a ,a + n , cmp );
        for(int i = 0; i < maxn ; i ++ ) b[i].clear();
        num = 0 ;
        int i ,j;
        i = 0 ;
        while(i < n )
        {
            num ++ ;
            val[num] = a[i].x;
            b[num].push_back(a[i].id);
            j = i + 1 ;
            while(a[j].x == a[i].x)
            {
                b[num].push_back(a[j].id);
                j ++ ;
            }
            i = j ;
        }

        int ans ;
        ans = 1 ;
       // printf("%d
",merge(b[1],b[2]));
        for(int i = 1 ; i <= num ; i ++ )
        {
            for(int j =  1  ; j <= num ; j ++ )
            if(i != j )
            {
                ans = max(ans , merge(b[i],b[j]));
            }
        }
        for(int i = 1 ; i <= num ; i++ )
            ans = max(ans , b[i].size());

        printf("%d
",ans);
    }
    return 0;
}

dp 

dp[i][j] = dp[j][last ] + 1 ; 

dp[i][j] 表示到达i位置,前一个数在j位置,显然, last是j前面的第一个与b[i]相同的位置即可(应该是满足单调的,前面的显然小于等于后面靠的近的)。这里可以把所有的不同的数取出来,然后二分,也可以直接线性扫描更新即可。

代码极其浓缩,很好。
http://codeforces.com/blog/entry/6161 这个下面的评论里讲的很赞。

int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i = 1;  i<= n; i ++ )
        {
            scanf("%d",&a[i]);
        }
        memset(f,0,sizeof(f));
        int ans ;
        ans = 0 ;
        for(int i = 1 ; i <= n ; i ++ )
        {
            int last = 0 ;
            for(int j = 0 ; j < i ; j ++ )
            {
                f[i][j] = f[j][last] + 1; // 
                if(a[i] == a[j]) last = j;
                ans = max(ans,f[i][j]);
            }
        }
        printf("%d
",ans);
    }
    return 0;
}

E:给一个n*n的格子,其中有个位置开了灯,每秒可以向相邻的四周开灯,这样一直下去,问最少多少秒,开够了c盏。

想的很乱,很复杂,没法实现,听了江哥的思路。

就是把平面划分成四部分,这样每一部分都是一样的子问题,只要解决在每一个小部分中的问题即可。

每个小部分有一下几种情况,我干脆画个图看得了,

在每个小部分中,有3种情况, a表示小矩形的短边,b表示长边,

每一部分解决后,整体的也就解决了。然后二分答案求解即可。

//////////////////////////////////下午了。。。。。。

 下午是专项训练,一些基础算法,一些陈题。

A: 只有2,3,5质因子的是丑数,求第n个丑数;

  暴力可以,打表也行,也有O(n)的做法。

  就是假设我们已经求出了前n-1个丑数(设第n-1为M),并且按顺序排在了数组中,现在求第n个丑数,那么第n个丑数必然是n-1个中的某些数的2,3,5倍 ,并且超过m的最小的元素,把这三个倍数中取出最小的就是第n个丑数

void init()
{
    int i1,i2,i3;
    i1 = 1;
    i2 = 1;
    i3 = 1;
    a[1] = 1;
    int i ;
    i = 2;
    while(i <= 1500)
    {
        a[i] = min(min(a[i1] * 2 ,a[i2] * 3 ) ,a[i3] * 5 );
        while(a[i1] * 2 <= a[i] ) i1 ++ ;
        while(a[i2] * 3 <= a[i] ) i2 ++ ;
        while(a[i3] * 5 <= a[i] ) i3 ++ ;
        i ++ ;
    }
}

B:牌排序,大模拟。

//0 < 1 < 2 < 3
//C < D < S < H
// 2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < 10 < 11 << 12 << 13 << 14
//2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < T < J < Q < K < A
struct node {
    int suit,rank;
};
char dx1[15] = {' ',' ','2','3','4','5','6','7','8','9','T','J','Q','K','A'};
char dx2[4] = {'C','D','S','H'};
node a[4][20]; // ¶« ÄÏ Î÷ ±±
int num[4];
int hash[256];
int _next;
bool cmp(node t1,node t2)
{
    if(t1.suit != t2.suit) return t1.suit < t2.suit;
    return t1.rank < t2.rank;
}
void print1()
{
    puts("+---+---+---+---+---+---+---+---+---+---+---+---+---+");
}
void print2(int j)
{
    printf("|");for(int i = 1 ; i <= 13 ; i ++ )  printf("%c %c|",dx1[a[j][i].rank],dx1[a[j][i].rank]);printf("
");
    printf("|");for(int i = 1 ; i <= 13 ; i ++ )  printf(" %c |",dx2[a[j][i].suit]);printf("
");
    printf("|");for(int i = 1 ; i <= 13 ; i ++ )  printf("%c %c|",dx1[a[j][i].rank],dx1[a[j][i].rank]);printf("
");
}
int main()
{
    hash['E'] = 0;
    hash['S'] = 1;
    hash['W'] = 2;
    hash['N'] = 3;

    char str[1000];
    char st[5];
    scanf("%s",st);
    while(st[0] != '#') {
        _next = hash[st[0]];
        _next = (_next + 1 ) % 4;
        int tt;
        tt = _next;
        memset(num ,0 ,sizeof(num));
        for(int x = 0 ; x < 2 ; x ++ ) {
            scanf("%s",str);
            for(int i = 0 ; i < 52 ; i += 2 ) {
                num[tt] ++ ;
                int v1,v2;
                if(str[i] == 'C') v1 = 0;
                else if(str[i] == 'D') v1 = 1;
                else if(str[i] == 'S') v1 = 2;
                else if(str[i] == 'H') v1 = 3;

                if(str[i+1] == 'A') v2 = 14;
                else if(str[i+1] == '2') v2 = 2;
                else if(str[i+1] == '3') v2 = 3;
                else if(str[i+1] == '4') v2 = 4;
                else if(str[i+1] == '5') v2 = 5;
                else if(str[i+1] == '6') v2 = 6;
                else if(str[i+1] == '7') v2 = 7;
                else if(str[i+1] == '8') v2 = 8;
                else if(str[i+1] == '9') v2 = 9;
                else if(str[i+1] == 'T') v2 = 10;
                else if(str[i+1] == 'J') v2 = 11;
                else if(str[i+1] == 'Q') v2 = 12;
                else if(str[i+1] == 'K') v2 = 13;

                a[tt][num[tt]].suit = v1;
                a[tt][num[tt]].rank = v2;
              //  printf("%d %d
",v1,v2);

                tt ++ ;
                tt %= 4;
            }
        }

        for(int i = 0 ; i < 4 ; i ++ ) {
            sort(a[i] + 1  ,a[i] + 14 ,cmp );
        }
       /* for(int i = 0 ; i< 4; i ++ )
        {
            printf("asdasdadn
");
            for(int j =1 ; j <= 13; j ++ )
            printf("%d %d",a[i][j].suit,a[i][j].rank);
        }
        puts("asd");
*/
        printf("South player:
");
        print1();
        print2(1);
        print1();

        printf("West player:
");
        print1();
        print2(2);
        print1();

        printf("North player:
");
        print1();
        print2(3);
        print1();

        printf("East player:
");
        print1();
        print2(0);
        print1();
        scanf("%s",st);
        if(st[0] != '#') puts("");
    }
    return 0;
}

C:poj上贪心的经典题型:poj1328 , 把点对应成区间,排序贪心即可。

#define maxn 2000
struct point
{
    double x,y;
};
point a[maxn];
struct seg
{
    double s,t;
};
seg b[maxn];
int num ;
int n ;
int d;

bool cmp(seg t1 ,seg t2)
{
    if(t1.s != t2.s ) return  t1.s < t2.s;
    return t1.t < t2.t;
}

int main()
{
    int cas ;
    cas = 0 ;
    while(scanf("%d%d",&n,&d)!=EOF && !(n == 0 && d == 0))
    {
        for(int i = 0 ; i < n ; i ++ )
            scanf("%lf%lf",&a[i].x,&a[i].y);
        bool flag;
        flag = true;
        num = 0 ;
        for(int i = 0 ; i < n ; i ++ )
        {
            if(a[i].y > d )
            {
                flag = false;
                break;
            }
            double tmp = d * d - a[i].y * a[i].y;
            tmp = sqrt(tmp);
            b[num].s = a[i].x - tmp;
            b[num].t = a[i].x + tmp;
            num ++ ;
        }
        if(flag == false )
        {
            printf("Case %d: -1
",++ cas);
            continue;
        }
      //  printf("num = %d
",num);
        sort(b , b + num , cmp );
       // for(int i = 0 ; i < num ; i ++ )
        {
       //    printf("%lf %lf
",b[i].s,b[i].t);
        }
        int ans;
        ans = 0 ;
        double _s , _t;
        _s = b[0].s;
        _t = b[0].t;
        for(int i = 1; i < num ; i ++ )
        {
           // printf("%lf %lf
",_s,_t);
            if(b[i].s >= _s && b[i].t <= _t)
            {
                _s = b[i].s;
                _t = b[i].t;
            }
            if( b[i].s >= _s && b[i].s <= _t && b[i].t > _t )
            {
                _s = b[i].s;
            }
            else if(b[i].s > _t )
            {
                ans ++ ;
                _s = b[i].s;
                _t = b[i].t;
            }
        }
        ans ++ ;
        printf("Case %d: %d
",++cas , ans);
    }
    return 0;
}

D:题意:有一个离散的函数,x取值1到N,告诉对应的y的取值,求两个点,满足要求:这两个点中间的点都在这两点连线下方并且使得这条连线的倾角最大。找出起始位置最小的这样的两个点。

分析:显然只要取相邻的两点就可以了,可以直接计算斜率,也可以比较相邻两条线段。
i64 qh(i64 h1,i64 w1,i64 h2 ,i64 w2)
{
    return w2 * h1 - w1 * h2 ;
}
int main()
{
    while(scanf("%d",&n)!=EOF) {
        for(int i = 1 ; i<= n ; i ++ )  scanf("%I64d",&a[i]);
        ans = a[2] - a[1] ;
        ans1 = 1;
        ans2 = 2;
        w = 1 ;
        for(int i = 3 ; i <= n ; i ++ ) {
            i64 tmp = qh(a[i] - a[i - 1] ,1 , ans , w);
            if(tmp > 0 ) {
                ans = a[i] - a[i -1];
                w = 1;
                ans1 = i - 1;
                ans2 = i ;
            }
            if(tmp < 0 ) {
                i64 tt = qh(a[i-1]-a[i],1,ans,w);
                if(tt > 0 ) {
                    ans = a[i-1] - a[i] ;
                    w = 1;
                    ans1 = i - 1;
                    ans2 = i ;
                }
            }

        }
        printf("%d %d
",ans1,ans2);
    }
    return 0;
}
刚开始理解有问题,因此写的有些复杂了。。。

E:求因子和,算是筛法吧。

#define maxn 1000050
int a,b;
i64 sum[maxn];
void init()
{
    for(int i = 0; i <= b; i ++ ) sum[i] = 0 ;
    for(int i = 1 ; i < b ; i ++ ) {
        int j = i * 2;
        while(j <= b) {
            sum[j] += i ;
            j += i ;
        }
    }
}
int main()
{
    scanf("%d%d",&a,&b);
    init();
    int ans;
    i64 sx;
    i64 tmp ;
    ans = a;
    sx = sum[a];
    for(int i = a + 1 ; i <= b ; i ++ ) {
      //  printf("%I64d
",sum[i]);
        tmp = sum[i] * ans - sx * i ;
        if(tmp < 0 ) {
            ans = i;
            sx = sum[i];
        }
    }
    printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/jh818012/p/3191989.html