Codeforces Round 1 10总结 【@Abandon】

CodeForces Beta Round #1 B Spreadsheets (字符串处理 && 进制处理)

弱炸了,这题都要做2个小时 T_T 。。。。。。

思路:先判断串中有两串数字还是一串数字,然后对应两种处理;再就是注意搞清楚题目中与通常有点儿不同的进制转换方法的处理。。。

View Code
#include <cstdio>
#include <cstring>
#include <string>
#include <math.h>
using namespace std;

const int N = 100010;
char s[N];
int L[N],R[N];
int lr;
int calnum(int x,int y)
{
    if (x > y)  return -1;
    int res = 0;
    bool flag = false;
    for (int i = x; i <= y; i ++)
    {
        if (flag)
            if (s[i] < '0' || s[i] > '9')
            {
                R[lr ++] = i - 1;
                res += calnum(i,y);
                return res;
            }
            else;
        else if (s[i] >= '0' && s[i] <= '9')
        {
            res ++;
            L[lr] = i;
            flag = true;
        }
    }
    R[lr ++] = y;
    return res;
}
int cal1(int l,int r)
{
    int res = 0;
    int base = 1;
    for (int i = r; i >= l; i --)
    {
        res += (s[i] - 'A' + 1) * base;
        base *= 26;
    }
    return res;
}
int cal2(int l,int r)
{
    int res = 0;
    for (int i = l; i <= r; i ++)
        res = res * 10 + s[i] - '0';
    return res;
}
void work1()
{
    int col = cal1(0,L[0] - 1);
    int row = cal2(L[0], R[0]);
    printf("R%dC%d\n",row,col);
}
int ff(char a[], int x, int n)
{
    int pos = 0;
    while(n)
    {
        n --;
        a[pos ++] = n % 26 + 'A';
        n /= 26;
    }
    return pos - 1;
}
void work2()
{
    int row,col;
    if (s[L[0] - 1] == 'R')
        row = cal2(L[0],R[0]),
        col = cal2(L[1],R[1]);
    else
    {
        col = cal2(L[0],R[0]);
        row = cal2(L[1],R[1]);
    }
    char a[5];
    memset(a,0,sizeof(a));
    int pos = ff(a,0,col);
    for (int i = pos; i >= 0; i --)
        putchar(a[i]);
    printf("%d\n",row);
}
int main()
{
    //freopen("0test.in","r+",stdin);

    int n ;
    scanf("%d",&n);
    while(n --)
    {
        lr = 0;
        scanf("%s",s);
        int k = calnum(0,strlen(s) - 1);
        if (k == -1)    return 0;
        if (k == 1)
            work1();
        else    work2();
    }
    return 0;
}

CodeForces Beta Round #2 A Winner ★(map && 模拟)

首先第一点是要读清题意=.= …… 。感觉还是一道比较好的题。

思路:先离线处理读一遍找出结束后最大的值,然后第二次读的时候找到最后是那个值并且第一个超过的人。

View Code
#include <iostream>
#include <cstdio>
#include <string>
#include <map>
#include <cstring>
using namespace std;

map <string ,int > m1,m2;
int score[1010];
string s[1010];
int main()
{
    int maxscore = 0;
    int n;
    cin>>n;
    m1.clear();
    m2.clear();
    for (int i = 0; i < n; i ++)
    {
        cin>>s[i];
        cin>>score[i];
        m1[s[i]] += score[i];
    }
    map<string, int>::iterator it;
    for (it = m1.begin(); it != m1.end(); it ++)
        if (it->second > maxscore)
            maxscore = it->second;
    for (int i = 0; i < n; i ++)
    {
        m2[s[i]] += score[i];
        if (m1[s[i]] == maxscore && m2[s[i]] >= maxscore)
        {
            cout<<s[i]<<endl;
            return 0;
        }
    }
    return 0;
}

CodeForces Beta Round #3 B Lorry ★(排序+贪心の背包)

思路:标准的多重背包?但是记录选取哪个物品比较麻烦。注意到这道题的重量只有1和2两种,所以可以用两个数组记录重量为1的物品和重量为2的物品。然后两个数组按容量从大到小排序。然后枚举重量为2的物品选几个,最后选出最大值。

View Code
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

struct mm
{
    int p;
    int id;
}a1[100010],a2[100010];
int n1,n2;
bool cmp(mm a, mm b)
{
    return a.p > b.p;
}
int n,v;
int w1[100010];
int main()
{
    scanf("%d%d",&n,&v);
    for (int i = 0; i < n; i ++)
    {
        int w,p;
        scanf("%d%d",&w,&p);
        if (w == 1)
        {
            a1[n1].p = p;
            a1[n1 ++].id = i + 1;
        }
        else
        {
            a2[n2]. p = p;
            a2[n2 ++].id = i + 1;
        }
    }

    sort(a1,a1+n1,cmp);
    sort(a2,a2+n2,cmp);

    w1[0] = 0;
    for (int i = 1; i <= n1; i ++)
        w1[i] = w1[i - 1] + a1[i - 1].p;

    int sum = 0;
    int res = 0;
    int y1 = 0,y2 = 0;
    for (int i = -1; i < min(v/2, n2); i ++)
    {
        if (i != -1)
            sum += a2[i].p;
        int q = v - (i + 1) * 2;
        if (q <= n1)
            if (res < sum + w1[q])
            {
                res = sum + w1[q];
                y1 = q;
                y2 = i + 1;
            }
            else;
        else if (res < sum + w1[n1])
        {
            res = sum + w1[n1];
            y1 = n1;
            y2 = i + 1;
        }
    }
    printf("%d\n",res);
    if (y1 + y2 > 0)
    {
        for (int i = 0; i < y1; i ++)
            printf("%d ",a1[i].id);
        for (int i = 0; i < y2; i ++)
            printf("%d ",a2[i].id);
        printf("\n");
    }
    return 0;
}

CodeForces Beta Round #3 C Tic-tac-toe (暴力判断)

看上去很简单,可还是WA了很多次,6Y。。。思路要清楚,要考虑到所有的情况。。(。。这题如果不看数据估计我很难AC。。。=.=。。。

View Code
#include <cstdio>
#include <cstring>
using namespace std;

int x,o,p;
char s[5][5];
int main()
{
    for (int i = 0; i < 3; i ++)
    {
        char ss[5];
        gets(ss);
        for (int j = 0; j < 3; j ++)
        {
            s[i][j] = ss[j];
            if (ss[j] == 'X')
                x ++;
            else if (ss[j] == '0')   o ++;
            else p ++;
        }
    }
    if (x - o > 1 || o - x > 0)
        printf("illegal\n");
    else
    {
        int xx = 0,oo = 0;
        for (int i = 0; i < 3; i ++)
        {
            int a = 0,b = 0;
            for (int j = 0; j < 3; j ++)
            {
                if (s[i][j] == 'X') a ++;
                else if (s[i][j] == '0')   b ++;
            }
            if (a == 3)     xx = 3;
            if (b == 3)     oo = 3;
        }
        for (int j = 0; j < 3; j ++)
        {
            int a = 0,b = 0;
            for (int i = 0; i < 3; i ++)
            {
                if (s[i][j] == 'X') a ++;
                else if (s[i][j] == '0')   b ++;
            }
            if (a == 3)     xx = 3;
            if (b == 3)     oo = 3;
        }
        int a = 0, b = 0;
        for (int i = 0; i < 3; i ++)
            if (s[i][2-i] == 'X')   a ++;
            else if (s[i][2-i] == '0')  b ++;
        if (a == 3)     xx = 3;
        if (b == 3)     oo = 3;
        a = 0;  b = 0;
        for (int i = 0; i < 3; i ++)
            if (s[i][i] == 'X')     a ++;
            else if (s[i][i] == '0')    b ++;
        if (a == 3)     xx = 3;
        if (b == 3)     oo = 3;

        if (xx == 3 && oo == 3)   {   printf("illegal\n");    return 0;}
        else if (xx == 3)
        {
            if (x == o) {printf("illegal\n");   return 0;}
            else printf("the first player won\n");  return 0;
        }
        else if (oo == 3)
        {
            if (x > o)  {   printf("illegal\n");    return 0; }
            else    printf("the second player won\n"); return 0;
        }

        if (p == 0) {   printf("draw\n");   return 0;}
        if (x > o)  {   printf("second\n"); return 0;}
        if (o == x)  {   printf("first\n");  return 0;}
    }
    return 0;
}

CodeForces Beta Round #4 B Before an Exam构造

题目大意:一个数,有N个盒子,给出每个盒子需要的最小的数以及最大的数,把这个数分配到N个盒子中使N个盒子都符合条件。不能分配则输出NO。

思路:最简单的构造了吧~~先把sum分给每天需要的最小值,如果不够分了则NO。再从头到尾把多下的给了让当天最大,如果最后都最高了还剩余则也是NO。否则就安排好了,输出每天的小时数就行了~~

View Code
#include <iostream>
#include <cstring>
using namespace std;

int main()
{
    int mint[40],maxt[40];
    int a[40];
    int d,sum;
    cin>>d>>sum;
    memset(mint,0,sizeof(mint));
    memset(maxt,0,sizeof(maxt));
    for (int i = 0; i < d; i ++)
        cin>>mint[i]>>maxt[i];
    for (int i = 0; i < d; i ++)
    {
        a[i] = mint[i];
        sum -= mint[i];
    }
    if (sum < 0)    cout<<"NO\n";
    else
    {
        for (int i = 0; i < d; i ++)
        {
            if (sum <= 0)
                break;
            if (sum < maxt[i] - mint[i])
            {
                a[i] += sum;
                sum = 0;
            }
            else
            {
                a[i] = maxt[i];
                sum -= maxt[i] - mint[i];
            }
        }
        if (sum > 0)    cout<<"NO\n";
        else
        {
            cout<<"YES\n";
            for (int i = 0; i < d - 1; i ++)
                cout<<a[i]<<" ";
            cout<<a[d-1]<<endl;
        }
    }

    return 0;
}

CodeForces Beta Round #4 C Registration system (map)

很简单的一道题,但是有很多细节处理要注意:比如求log10n那儿,不可以把log(n)/log(10)直接赋给int,因为实际算下来的double值是2.9999999……(汗了…),一个小技巧就是我们给n加个0.1。

View Code
#include <iostream>
#include <string>
#include <cstring>
#include <map>
#include <cmath>
using namespace std;

#define eps 1e-1
map <string ,int> m;
char a[100010];
void ff(char a[], int x)
{
    double l = log(double(x + eps))/log(double(10));
    int p = l;
    while(x)
    {
        a[p--] = x % 10 + '0';
        x /= 10;
    }
}
int main()
{
    m.clear();
    string s;
    int n;
    cin>>n;
    while(n--)
    {
        cin>>s;
        if (m[s] == 0)
        {
            m[s] ++;
            cout<<"OK\n";
        }else
        {
            memset(a,0,sizeof(a));
            int p = m[s];
            ff(a,p);
            m[s] ++;
            s = s + string(a);
            cout<<s<<endl;
        }
    }
    return 0;
}

CodeForces Beta Round #4 D Mysterious Present (DP最长上升子序列)

题目大意:N张牌,有两个权值,可以安排他们的顺序。求出最长的牌序列,使得他们的权值递增,并且使得card(可以理解为一张特殊的牌)插到头部后仍然符合条件。

两个限制的最长上升子序列(反着做,方便处理插入card),和一个限制是一样的……然后因为他不是固定的序列而是可以自己安排顺序,所以一开始应该按两位从小到大排序。然后找答案就找两值都大于card的且上升序列最大的一处。然后还有记录路径什么的……

View Code
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

struct envelop
{
    int w,h;
    int id;
}e[5005];
bool cmp(envelop a, envelop b)
{
    if (a.w != b.w)
        return a.w > b.w;
    else
        return a.h > b.h;
}
int main()
{
    int f[5005];
    int pre[5005];
    memset(pre,-1,sizeof(pre));
    int n,w,h;
    cin>>n>>w>>h;
    for (int i = 0; i < n; i ++)
    {
        cin>>e[i].w>>e[i].h;
        e[i].id = i + 1;
    }

    sort(e,e+n,cmp);
    for (int i = 0; i < n; i ++)
        f[i] = 1;
    for (int i = 1; i < n; i ++)
        for (int j = 0; j < i; j ++)
            if (e[j].w > e[i].w && e[j].h > e[i].h && f[j] + 1 > f[i])
            {
                pre[i] = j;
                f[i] = f[j] + 1;
            }
    int k = -1;
    int maxn = 0;
    for (int i = 0; i < n; i ++)
        if (f[i] > maxn && e[i].w > w && e[i].h > h)
        {
            k = i;
            maxn = f[i];
        }
    if (k == -1)
    {
        cout<<0<<endl;
        return 0;
    }
    cout<<maxn<<endl;
    int a[5005];
    int p = pre[k];
    a[0] = e[k].id;
    int num = 1;
    while(p != -1)
    {
        a[num ++] = e[p].id;
        p = pre[p];
    }
    for (int i = 0; i < maxn - 1; i ++)
        cout<<a[i]<<" ";
    cout<<a[maxn - 1]<<endl;
    return 0;
}

CodeForces Beta Round #5 C Longest Regular Bracket Sequence ★(栈扫描)

题目大意:一个括号序列,求合法的最长连续括号匹配子序列,以及有几个这样的子括号序列。

思路:很不错的一道题,括号匹配的升级版吧~~做法就是基于栈的扫描。

一开始的想法是从左向右扫描,当遇到左括号时加入栈;遇到右括号,如果栈中有左括号则可以匹配,当前括号长度n加2,如果栈中为空则这个右括号无法匹配,则前面的序列到这儿就断了,所以把n、maxn统计一下,然后清零开始下一位。………………但是这个想法马上就被一个testdata证明是不周到的:(((())()。

发现问题了么?就是不是只有右括号会造成断列,前面的左括号如果找不到匹配也会断列。所以我们不能光吧左括号直接加到栈中就完事儿了。这里我的想法就是每次加进去左括号时统计一下它左边连续的括号序列,而且立刻把当前n清零不让它直接参与后面的序列连续(因为这个左括号能不能匹配还不一定对吧~),然后当我们扫描到一个右括号可以和这个左括号匹配时再把那个记录下的n加进来~~

View Code
#include <iostream>
#include <stack>
#include <cstring>
using namespace std;

stack <pair<char,int> > t;
int main()
{
    int maxn = 0,maxnum = 1;
    int n = 0;
    string s;
    cin>>s;
    for (int i = 0; i < s.size(); i ++)
    {
        if (s[i] == '(')
            {
                t.push(make_pair(s[i], n));
                n = 0;
            }
        else
        {
            if (t.empty())
                n = 0;
            else
            {
                int u = t.top().second;
                t.pop();
                n += u + 2;
                if (n > maxn)
                {
                    maxn = n;
                    maxnum = 1;
                }
                else if (n != 0 && n == maxn)
                    maxnum ++;
            }
        }
    }
    cout<<maxn<<" "<<maxnum<<endl;
}

CodeForces Beta Round #5 E Bindian Signalizing

题目大意:一圈有身高的人,如果两人之间不存在比他俩都高的人则他俩能相互看见,求能相互看见的有几对。。。

思路:很经典的一道题。首先把圈变成线性处理:找出身高最高的人(如果有相同的则随便找一个),从他割开。然后我们维护三个值:r[i]表示第i位右边第一个比他大的数;l[i]表示第i位左边第一个比他大的数;c[i]表示从i到r[i]和i相等的数。

对于每一对数,我们总是按小的(如果他们相等则按位置小的)考虑来添加符合条件的一对儿。那么对于x,符合一下3种情况的另一个数可以和他组成符合条件的一对:①x左边第一个比他大的数w(l[i]) ②x右边第一个比他大的数y(r[i]) ③从x到y那些和他相等的数(c[i])。

如何求i右边(左边)第一个比他大的数?这个问题在USACO中有一道类似的题,处理方法就是:

c[n] = 0;
for(int i = n - 1; i >= 0; --i) {
    r[i] = i + 1;
    while (r[i] < n && height[i] > height[r[i]]) r[i] = r[r[i]];
    if (r[i] < n && height[i] == height[r[i]]) {
        c[i] = c[r[i]] + 1;
        r[i] = r[r[i]];
    }
}
View Code
#include <iostream>
#include <cstring>
using namespace std;

const int N = 1000100;
long long l[N],r[N],a[N],b[N],c[N];
int main(){
    int n;
    cin>>n;
    for (int i = 0; i < n; i ++){
        cin>>b[i];
    }
    int max = 0,maxn = 0;
    for (int i = 0; i < n; i ++){
        if (b[i] > max){
            max = b[i];
            maxn = i;
        }
    }
    int j = 0;
    for (int i = maxn + 1; i < n; i ++){
        a[j ++] = b[i];
    }
    for (int i = 0; i <= maxn; i ++){
        a[j ++] = b[i];
    }
    memset(l,-1,sizeof(l));
    memset(r,-1,sizeof(r));
    memset(c,0,sizeof(c));
    for (int i = n - 2; i >= 0; i --){
        int p = i + 1;
        while(p != -1 && p != i && a[p] <= a[i]){
            if (p != i && a[p] == a[i]){
            c[i] = c[p] + 1;
            }
            p = r[p];
        }
        if (p != i && a[p] > a[i]) r[i] = p;
    }
    if (a[0] < a[n-1])    l[0] = n - 1;
    for (int i = 1; i < n; i ++){
        int p = i - 1;
        while(p != -1 && p != i && a[p] <= a[i]){
            p = l[p];
            }
        if (p != i && a[p] > a[i]){
            l[i] = p;
        }
    }
    long long ans = 0;
    for (int i = 0; i < n; i ++)
    {
        ans += c[i];
        if (l[i] == -1 || r[i] == -1)
        {
            if (l[i] == -1 && r[i] == -1)
                continue;
            else    ans ++;
        }
        else if (l[i] == n - 1 && r[i] == n - 1)
            ans ++;
        else
            ans += 2;
    }
    cout<<ans<<endl;
    return 0;
}

(未完待续。。。>.<)

举杯独醉,饮罢飞雪,茫然又一年岁。 ------AbandonZHANG
原文地址:https://www.cnblogs.com/AbandonZHANG/p/2742990.html