第二届“传智杯”全国大学生IT技能大赛(初赛)

P6363 软件工程实习

思路

  • 分析:这一题就是一个模拟题,按照他说的过程一步一步的来

代码

#include
#include
#include
#include
#include
using namespace std;

#define ll long long 
#define db double
#define INF 0x3f3f3f3f
const int mxn = 2005;
int n, k;

struct Node
{
    db a, b;
    int x;
    int sum;
    bool operator < (const Node z) const
    {
        if(sum == z.sum)
            return x < z.x;
        return sum > z.sum;
    }
} st[mxn];

db mrk[mxn][mxn];
db ar[mxn], arr[mxn];
int brr[mxn];    //统计每个队伍的数量
char c[2];


int main()
{
    /* freopen("A.txt", "r", stdin); */
    /* freopen("Ans.txt", "w", stdout); */

    scanf("%d %d", &n, &k);
    for(int i = 1; i <= n; i ++)
    {
        scanf("%lf %s", &st[i].a, c), st[i].a *= 0.6;
        st[i].x = c[0] - 'A' + 1;
    }

    for(int i = 1; i <= k; i ++)
    {
        for(int j = 1; j <= k; j ++)
            scanf("%lf", &mrk[i][j]), ar[j] += mrk[i][j];
    }
    
    for(int i = 1; i <= k; i ++)
    {
        ar[i] /= k;
    }
    for(int i = 1; i <= k; i ++)
    {
        for(int j = 1; j <= k; j ++)
        {
            if(mrk[i][j] >= ar[j] - 15 && mrk[i][j] <= ar[j] + 15)
                arr[j] += mrk[i][j], brr[j] ++;
        }
    }

    for(int i = 1; i <= k; i ++)
    {
        arr[i] = round(arr[i]/brr[i]);
    }

    for(int i = 1; i <= n; i ++)
    {
        st[i].b = arr[st[i].x] * 0.4;
        st[i].sum = round(st[i].a + st[i].b);
    }

    sort(st + 1, st + 1 + n);
    for(int i = 1; i <= n; i ++)
    {
        printf("%d %c\n", st[i].sum, st[i].x + 'A' - 1);
    }

    return 0;
}

P6364 1024 程序员节发橙子

思路

  • 贪心思路
  1. 这一题我们如果用贪心的思路来做的话,初始的时候每个人一个橘子,先从左向右扫一遍,如果当前的这个人的分数比左边的高的话,就在左边的人的橘子数量上加1,如果过相同的话,直接 让当前这个人的橘子的数量等于左边的那个同学的数量。
  2. 从左向右向左在扫一遍,如果当前这个同学的分数比右边同学的分数高,并且橘子的数量却是小于等于相邻右边的那个同学的话,这个时候在让右边的同学的数量上+1,就行了
  • 差分约束:这一题如果用差分约束的话,它的本质的就是解不等式,
  1. 首先这一题考虑,让我们求总共最小的橘子的数量,那么我们应该用 Dijkstra或Spfa 跑长短路,所以我们要把所有的不等式转化 “ >= ”的形式,然后建立边
  2. 那么我们可以从题意上找出隐含的不等式,如果 当前同学(v 为其橘子的数量)的分数 == 左边同学(u 为其橘子的数量)的分数的,那么说明 v == u, 那么我们建立权值为为0的双向边,如果分高于的话v - u >= 1 建立 i-1 -> i 权值为1 的边,如果分低于的话那么 u - v >= 1,建立 i -> i -1 权值为1的反向边

代码(贪心)

#include
#include
#include
#include
#include
#include
using namespace std;

#define ll long long 
const int mxn = 1e6 + 10;
int ar[mxn];
ll br[mxn];


int main()
{
    /* freopen("A.txt","r",stdin); */
    /* freopen("Ans.txt","w",stdout); */
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++)
        scanf("%d", &ar[i]), br[i] = 1;
    
    for(int i = 2; i <= n; i ++)
    {
        if(ar[i] > ar[i - 1])
            br[i] = br[i - 1] + 1;
        else if(ar[i] == ar[i - 1])
            br[i] = br[i - 1];
    }

    for(int i = n - 1; i >= 1; i --)
    {
        if(ar[i] > ar[i + 1] && br[i] <= br[i + 1])
            br[i] = br[i + 1] + 1;
        else if(ar[i] == ar[i + 1])
            br[i] = max(br[i], br[i + 1]);
    }

    ll sum = 0;
    for(int i = 1; i <= n; i ++)
        sum += br[i];
    printf("%lld\n", sum);

    return 0;
}

代码(最长路-差分约束)

#include
#include
#include
#include
#include
#include
using namespace std;

#define ll long long 
#define db double
#define INF 0x3f3f3f3f
const int mxn = 1e6 + 10;
int ar[mxn];
int n;

struct Edge
{
    int v, w, next;
} edge[mxn * 4];
int head[mxn];
int k = 0; 
struct Node
{
    int x, dis;
    bool operator < (const Node a) const 
    {
        return dis < a.dis;
    }
};

void Add(int u, int v, int w)
{
    edge[++ k] = (Edge){ v, w, head[u] };
    head[u] = k;
}

int dis[mxn], use[mxn];
void Spfa(int s)
{
    for(int i = 0; i <= n; i ++)
        dis[i] = -INF, use[i] = 0;
    dis[s] = 0;
    priority_queue<Node> q;
    q.push((Node){ s, dis[s] });
    int u, v, w;
    while(! q.empty())
    {
        u = q.top().x; q.pop();
        use[u] = 0;

        for(int i = head[u]; i; i = edge[i].next)
        {
            v = edge[i].v;
            w = edge[i].w;
            if(dis[v] < dis[u] + w)
            {
                dis[v] = dis[u] + w;
                if(! use[v])
                {
                    q.push((Node){ v, dis[v] });
                    use[v] = 1;
                }
            }
        }
    }
}


int main()
{
    /* freopen("A.txt", "r", stdin); */
    /* freopen("Ans.txt", "w", stdout); */
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++)
        scanf("%d", &ar[i]);
    for(int i = 2; i <= n; i ++)
    {
        if(ar[i] == ar[i - 1])
        {
            Add(i-1, i, 0);
            Add(i, i-1, 0);
        }
        else if(ar[i] > ar[i - 1])
        {
            Add(i-1, i, 1);           // v >= 1 + u
        }
        else
        {
            Add(i, i-1, 1);                     // u - v >= 1; u >= v + 1
        }
        
        /* if(ar[i] == ar[i + 1]) */
        /* { */
        /*     Add(i, i+1, 0); */
        /*     Add(i+1, i, 0); */
        /* } */
        /* else if(ar[i] > ar[i + 1]) */
        /* { */
        /*     Add(i+1, i, 1); */
        /* } */
        /* else */
        /* { */
        /*     Add(i, i+1, 1); */
        /* } */
    }
    for(int i = 1; i <= n; i ++)
    {
        Add(0, i, 1);
    }
    Spfa(0);
    ll sum = 0;
    for(int i = 1; i <= n; i ++)
        sum += dis[i];
    printf("%lld\n", sum);

    return 0;
}

P6365 众数出现的次数

思路

  • 分析:对于这一题给出的两张牌我们可以稍作改变,第一张的值不变,第二张的值变为 第一张的值疑惑第二章的值 的结果,这样在用 map统计 每个人手中牌的的数字出现的次数,如果两张牌数字相同,只统计一次,,,

代码

#include
#include
#include
#include
#include
#include
using namespace std;

#define ll long long 
#define db double
#define INF 0x3f3f3f3f
const int mxn = 1e6 + 10;
int ar[mxn], br[mxn];
int n;
map<int, int> mp;




int main()
{
    /* freopen("A.txt", "r", stdin); */
    /* freopen("Ans.txt", "w", stdout); */
    scanf("%d", &n);
    int x, y;
    for(int i = 1; i <= n; i ++)
    {
        scanf("%d %d", &x, &y);
        ar[i] = x;
        br[i] = x^y;
        mp[ar[i]] ++;
        if(ar[i] != br[i])
            mp[br[i]] ++;
    }
    int mx = -1, val;
    for(auto x : mp)
    {
       if(mx < x.second) 
       {
           mx = x.second;
           val = x.first;
       }
    }
    printf("%d\n", val);  

    return 0;
}


P6366 特殊的翻转

思路

  • 分析
  1. 先把所给的数转化为二进制(直接看代码部分)
  2. 这属于开关问题,有一些东西要注意一下,对于某个要反转的位置如果 我们反转两次以上是没有意义的,其次对于 各个 翻转操作的顺序,谁在前,谁在后并不影响结果。
  3. 对这一题如果我们不考虑开头和结尾的特殊情况(可以反转两个,不必反转三个),那么对于中间的要反转的情况,那么我们都是一下反转相邻的三位,对于中间位置从左向右开始我们一旦遇到1,说明就必须要将这个位置及以后的2位都进行反转(不反转的化,是不符合要求的),这样问题的规模就减小了1,然后依次向右遍历 遇见1就反转就行了
  4. 现在我们考虑开头的两种情况:反转开头2位 、反转开头连续的3位,我们对两种情况分别进行,尝试翻转就行了,显然这两种情况都有可能反转出符合题意的状态,但是可能的反转的次数不一样,这个时候我们去反转的次数小的那个数就行了,如果只有一个能反转出全0状态,那就输出那个结果,如果都不能反转出全0状体,输出 No
  5. 对于结尾也是于开头,一样可以反转两位或者反转三位,这个时候我们操作是在所转化的字符串末尾 补个0,这样就避免了 这两种情况的选择的烦恼

代码

#include
#include
#include
#include
#include
#include
using namespace std;

const int mxn = 1e6 + 10;
map<char, string> mp, tr;
char ar[mxn];
int br[mxn << 2];


int main()
{
    //freopen("A.txt","r",stdin);
    //freopen("Ans.txt","w",stdout);
    mp['0'] = "0000"; tr['0'] = "0000";
    mp['1'] = "0001"; tr['1'] = "1000";
    mp['2'] = "0010"; tr['2'] = "0100";
    mp['3'] = "0011"; tr['3'] = "1100";
    mp['4'] = "0100"; tr['4'] = "0010";
    mp['5'] = "0101"; tr['5'] = "1010";
    mp['6'] = "0110"; tr['6'] = "0110";
    mp['7'] = "0111"; tr['7'] = "1110";
    mp['8'] = "1000"; tr['8'] = "0001";
    mp['9'] = "1001"; tr['9'] = "1001";
    mp['A'] = "1010"; tr['A'] = "1010";
    mp['B'] = "1011"; tr['B'] = "1101";
    mp['C'] = "1100"; tr['C'] = "0011";
    mp['D'] = "1101"; tr['D'] = "1011";
    mp['E'] = "1110"; tr['E'] = "0111";
    mp['F'] = "1111"; tr['F'] = "1111";

    scanf("%s", ar);
    string s;
    int n = strlen(ar);

    //转化为二进制操作(注意通过 & 操作符号把所有的不是考虑的当前二进制为删除)
    //for(int i = 0; i < n; i ++)
    //{
    //    if(ar[i] > 64) ar[i] -= 7;
    //    br[4*i + 0] = ((ar[i] - 48)&8)/8;
    //    br[4*i + 1] = ((ar[i] - 48)&4)/4;
    //    br[4*i + 2] = ((ar[i] - 48)&2)/2;
    //    br[4*i + 3] = ((ar[i] - 48)&1)/1;
    //}
    //去前导0
    //int digit = 0;
    //while(br[digit] == 1)
    //    digit ++;

    //另一种转化为二进制的方法
    s += "0";
    for(int i = 0; i < n; i ++)
        s += mp[ar[i]];
    s += "0";			//补0操作
    int l = 0, r = s.size() - 1;
    while(s[l] == '0')
        l ++;
    s[l - 1] = '1';

    string ss = s;
    int ans1 = 0;
    //第一次修改前两个
    for(int i = l; i < r; i ++)
    {
        if(s[i - 1] == '1')
        {
            s[i - 1] = '0';
            ans1 ++;
            if(s[i] == '1')
                s[i] = '0';
            else
                s[i] = '1';

            if(s[i+1] == '1')
                s[i+1] = '0';
            else
                s[i+1] = '1';
        }
    }
    for(int i = l; i < r; i ++)
        if(s[i] == '1')
        {
            ans1 = -1;
            break;
        }

    int ans2 = 0;
    //第一次修改前三个
    for(int i = l + 2; i <= r; i ++)
    {
        if(ss[i - 2] == '1')
        {
            ss[i - 2] = '0';
            ans2 ++;
            if(ss[i - 1] == '1')
                ss[i - 1] = '0';
            else 
                ss[i - 1] = '1';

            if(ss[i] == '1')
                ss[i] = '0';
            else
                ss[i] = '1';
        }
    }

    for(int i = l; i < r; i ++)
    {
        if(ss[i] == '1')
        {
            ans2 = -1;
            break;
        }
    }

    if(ans1 == -1 && ans2 == -1)
        printf("No\n");
    else if(ans1 == -1)
        printf("%d\n", ans2);
    else if(ans2 == -1)
        printf("%d\n", ans1);
    else
        printf("%d\n", min(ans1, ans2));

    return 0;
}

  • 总结
    1.学会了一个转化为二进制操作
//转化为二进制操作(注意通过 & 操作符号把所有的不是考虑的当前二进制为删除)
    //for(int i = 0; i < n; i ++)
    //{
    //    if(ar[i] > 64) ar[i] -= 7;
    //    br[4*i + 0] = ((ar[i] - 48)&8)/8;
    //    br[4*i + 1] = ((ar[i] - 48)&4)/4;
    //    br[4*i + 2] = ((ar[i] - 48)&2)/2;
    //    br[4*i + 3] = ((ar[i] - 48)&1)/1;
    //}
    //去前导0
    //int digit = 0;
    //while(br[digit] == 1)
    //    digit ++;
  1. 注意 分类讨论,与补0操作,使复杂的操作变的简单些

你可能感兴趣的:(水题)