2013暑假集训B组训练赛第一场

A.Permutation

思路:这题比较水,计数算出1-n中重复的数的个数就是最少的改变次数

#include 
#include 
const int maxn = 5000+10;
bool has[maxn];
int n, ans, tmp;
int main()
{
    while(scanf("%d", &n) != EOF)
    {
        ans = 0;
        memset(has, false, sizeof(has));
        for(int i = 0; i < n; i++)
        {
            scanf("%d", &tmp);
            if(tmp < 1 || tmp > n)
                ans++;
            else if(has[tmp])//重复的数用来修改
                ans++;
            else
                has[tmp] = true;
        }
        printf("%d\n", ans);
    }
    return 0;
}


B. 这道题因为给出的区间多,而需要求公约数的两个数是确定的,所以我们可以预处理求出所有的公约数(所有最大公约数的约数)需要O(N^0.5)

公约数存在arr[]数组中,这样每个询问相当于在arr[]中找到一个最大的数使他在询问要求的区间中。可以把arr[]排序后二分查找这个数。

#include 
#include 
#include 
using namespace std;
const int maxn = 40000+10;
int a, b, n, gcd, low, hig;
int arr[maxn*2], cnt;
int Gcd(int a, int b)
{
    if(b == 0)
        return a;
    return Gcd(b, a % b);
}
void GetYue(int tar)//求最大公约数的所有约数
{
//    int up = floor(sqrt((double)tar));
//    cnt = 0;
//    if(tar % up == 0)
//        arr[cnt++] = up;
//    for(int i = 1; i < up; i++)
//        if(tar % i == 0)
//            arr[cnt++] = i, arr[cnt++] = tar / i;
    for(int i = 1; i*i <= tar; i ++){
        if(tar%i==0){
            arr[cnt++] = i;
            if(tar/i!=i) arr[cnt++] = tar/i;
        }
    }
    sort(arr, arr+cnt);
}
int Query(int lo, int hi)
{
//    int p = upper_bound(arr, arr+cnt, hi) - arr - 1;
//        return arr[p] >= lo ? arr[p] : -1;
    int l = 0, r = cnt-1;
    while(l < r)
    {
        int mid = r-(r-l)/2;
        if(arr[mid] < lo)
            l = mid+1;
        else if(arr[mid] > hi)
            r = mid-1;
        else
            l = mid;
    }
    if(l > r || arr[l] < lo || arr[l] > hi)
        return -1;
    else
        return arr[l];
}
int main()
{
    while(scanf("%d%d", &a, &b) != EOF)
    {
        scanf("%d", &n);
        gcd = Gcd(a, b);
        GetYue(gcd);
        for(int i = 0; i < n; i++)
        {
            scanf("%d%d", &low, &hig);
            printf("%d\n", Query(low, hig));
        }
    }
    return 0;
}

C.Fancy Number

数字只有0-9十个,所以我们可以枚举最后相同的数, 然后选出最优解

我们以最后达到10个5为目标,现在又2个5,则要把8个其他数字改为5,这题以花费最少、其次字典序最小为目标,所以遵循几个原则:

1.首先我们改变的数字和目标数字(5)的绝对值要最小(最小花费)

2.当两个数绝对值相同时,先把比目标数字大的数改为目标数字(这样字典序变小了)

3.当两个数相同时,如果这个数比目标数字大,先改变高位那个(字典序变小的多);如果这个数比目标数小则改变地位那个(字典序变大的少)

4.对求出来的十个答案进行比较(花费和字典序)

#include 
#include 
const int maxn = 10000+10;
int len, k;
char str[maxn], ans[10][maxn];
int cnt[maxn], cost[10];
int chn[10];//改变dif[i]个差值为i的数
bool OK()
{
    for(int i = 0; i < 10; i++)
        if(cnt[i] >= k)
            return true;
    return false;
}
void ChangeTo(int tar)
{
    int res = k - cnt[tar], dif = 1;
    memset(chn, 0, sizeof(chn));
    while(true)
    {
        if(res <= 0)
            break;
        if(tar+dif < 10)
        {
            if(cnt[tar+dif] >= res)
                chn[tar+dif] = res, res = 0;
            else
                chn[tar+dif] = cnt[tar+dif], res -= cnt[tar+dif];
        }

        if(res <= 0)
            break;
        if(tar-dif >= 0)
        {
            if(cnt[tar-dif] >= res)
                chn[tar-dif] = res, res = 0;
            else
                chn[tar-dif] = cnt[tar-dif], res -= cnt[tar-dif];;
        }
        dif++;
    }
    cost[tar] = 0;
    for(int i = 0; i < 10; i++)
        if(chn[i])
            cost[tar] += chn[i] * (tar > i ? tar - i : i - tar);
    for(int i = 0; i < len; i++)
    {
        ans[tar][i] = str[i];
        int num = str[i] - '0';
        if(num > tar && chn[num])//改变小的
            ans[tar][i] = tar + '0', chn[num]--;
    }
    for(int i = len-1; i >= 0; i--)
    {
        int num = str[i]-'0';
        if(num < tar && chn[num])
            ans[tar][i] = tar + '0', chn[num]--;
    }
    ans[tar][len] = 0;
}
bool Cmp(int id1, int id2)
{
    if(cost[id1] < cost[id2])
        return true;
    else if(cost[id1] > cost[id2])
        return false;
    for(int i = 0; i < len; i++)
    {
        if(ans[id1][i] < ans[id2][i])
            return true;
        else if(ans[id1][i] > ans[id2][i])
            return false;
    }
    return false;
}
int main()
{
    while(scanf("%d%d", &len, &k) != EOF)
    {
        scanf("%s", str);
        memset(cnt, 0, sizeof(cnt));
        for(int i = 0; i < len; i++)
            cnt[str[i]-'0']++;
        if(OK())
        {
            printf("%d\n%s\n", 0, str);
            continue;
        }
        for(int i = 0; i < 10; i++)//枚举相同的数
            ChangeTo(i);
        int ansId = 0;
        for(int i = 1; i < 10; i++)
        {
            if(Cmp(i, ansId))//答案i更优
                ansId = i;
        }
        printf("%d\n%s\n", cost[ansId], ans[ansId]);
    }
    return 0;
}


D.Crashing Robots
模拟题直接按题意一步一步来……
#include 
const int maxn = 100+10;
struct Rob{
    int x, y, dir;
};
Rob rob[maxn];
int dx[] = {1, 0, -1, 0};//ESWN东南西北
int dy[] = {0, -1, 0, 1};
int matrix[maxn][maxn], A, B;
int tcase, N, M;
int DIR(char cc)
{
    if(cc == 'E') return 0;
    else if(cc == 'S') return 1;
    else if(cc == 'W') return 2;
    else return 3;
}
bool GoNext(int id, char cmd, int cnt)
{
    if(cmd == 'L')
    {
        rob[id].dir = (rob[id].dir-cnt+100)%4;
        return false;//没有失败
    }
    else if(cmd == 'R')
    {
        rob[id].dir = (rob[id].dir+cnt)%4;
        return false;
    }
    else
    {
        int nowx = rob[id].x, nowy = rob[id].y, dd = rob[id].dir;
        for(int i = 0; i < cnt; i++)
        {
            int ntx = nowx + dx[dd], nty = nowy + dy[dd];
            if(ntx < 1 || ntx > A || nty < 1 || nty > B)//撞墙
            {
                printf("Robot %d crashes into the wall\n", id);
                return true;//失败了
            }
            else if(matrix[ntx][nty] != 0)//撞机器
            {
                printf("Robot %d crashes into robot %d\n", id, matrix[ntx][nty]);
                return true;
            }
            else
                nowx = ntx, nowy = nty;//可以走
        }
        matrix[nowx][nowy] = id;
        matrix[rob[id].x][rob[id].y] = 0;
        rob[id].x = nowx, rob[id].y = nowy;
        return false;
    }
}
int main()
{
    char cmd[5];
    int id, cnt;
    scanf("%d", &tcase);
    while(tcase--)
    {
        scanf("%d%d%d%d", &A, &B, &N, &M);
        for(int i = 1; i <= A; i++)
            for(int j = 1; j <= B; j++)
                matrix[i][j] = 0;
        for(int i = 1; i <= N; i++)
        {
            scanf("%d%d%s", &rob[i].x, &rob[i].y, cmd);
            rob[i].dir = DIR(cmd[0]);
            matrix[rob[i].x][rob[i].y] = i;
        }
        bool fail = false;//前面的操作是否已经失败了
        for(int i = 1; i <= M; i++)
        {
            scanf("%d%s%d", &id, cmd, &cnt);
            if(fail)
                continue;
            fail = GoNext(id, cmd[0], cnt);
        }
        if(!fail)
            printf("OK\n");
    }
    return 0;
}


E.Parencodings

#include 
const int maxn = 20+10;
char str[maxn*2];
int n, tcase;
int main()
{
    scanf("%d", &tcase);
    while(tcase--)
    {
        scanf("%d", &n);
        int has = 0, len = 0, num;//has已有的'('个数
        for(int i = 0; i < n; i++)
        {
            scanf("%d", &num);
            for(; has < num; has++)
                str[len++] = '(';
            str[len++] = ')';
        }
	//先还原字符串
        bool first = true;
        for(int i = 0; i < len; i++)
        {
            if(str[i] == ')')
            {
                int cnt = 1, j;
                for(j = i-1; str[j] != '('; j--)
                    if(str[j] == 'R')
                        cnt++;
                str[j] = 'L', str[i] = 'R';//已匹配的括号做标记
                if(!first)
                    putchar(' ');
                first = false;
                printf("%d", cnt);
            }
        }
        puts("");
    }
    return 0;
}

F.Not a Triangle

2000条线段,直接枚举三条边肯定爆。

这样我们就排序后枚举三角形中小的两条边a, b为num[x1], num[x2],

然后第三条边(最大的边)肯定在num[x2+1]~num[n-1]中,二分查找可以做第三条边的边数,N*N*ln(N)

#include 
#include 
#include 
using namespace std;
const int maxn = 2000+10;
int len[maxn], n;
int Find(int l, int r, int tt)//在[l, r]寻找>tt的个数
{
    if(l > r)
        return 0;
    int up = upper_bound(&len[l], &len[r]+1, tt)-&len[l];
    if(len[l+up] == INT_MAX)
        return 0;
    else
        return (r-l+1)-up;
}
int main()
{
    while(scanf("%d", &n) != EOF && n)
    {
        for(int i = 0; i < n; i++)
            scanf("%d", &len[i]);
        sort(len, len+n);
        int ans = 0;
        len[n] = INT_MAX;
        for(int i = 0; i < n; i++)
            for(int j = i+1; j < n; j++)
                ans+=Find(j+1, n-1, len[i]+len[j]);
        printf("%d\n", ans);
    }
    return 0;
}




你可能感兴趣的:(2013暑假集训B组训练赛第一场)