The 14th Zhejiang University Programming Contest(未完工)

链接:The 14th Zhejiang University Programming Contest

【2014/05/10】今下午和队友一起做了这套题,最后的Rank差不多这样,AC数目4道。现在就先把做了的4道题的心得写下来,剩下几道题以后会更新。

【2014/05/12】今天完成了Diablo III这道题,看了别人的解题报告后,真是自愧不如啊,dp果然是神奇的东西。

【2014/05/13】今天完成了Calcuate the Function这道题,矩阵真是强大,只怪线性代数学的撇。=_=#



A - Elevator

这是一道最大的水题,zzy学长一看题就敲了,然后就pass了。代码如下:

/*
** ZOJ 3767 Elevator
** Created by Rayn @@ 2014/05/10
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int main() {

    int t;
    scanf("%d", &t);
    while(t--)
    {
        int n, m, sum = 0;
        scanf("%d%d", &n, &m);
        for (int i=0; i<n; i++)
        {
            int x;
            scanf("%d", &x);
            sum += x;
        }
        if (sum > m)
        {
            printf("Warning\n");
        }
        else
        {
            printf("Safe\n");
        }
    }
    return 0;
}


B - Continuous Login

【题意】给你一个整数N,然后将N表示为若干段1~K连续的数段的和,问你最小的段数,输出几段的K。

【思路】开始看这道题觉得是贪心或者DP吧,然后我想了一下觉得不会出现第四段,最后打了一下表发现果然最多只有三段。于是这样就可以暴力了。先把连续数段和处理出来,然后直接lower_bound查找第一段数,符合就直接输出,不行就枚举两段的和三段的。最后60MS过了,按道理来说复杂度很高的。下面是代码:

/*
** ZOJ 3768 Continuous Login
** Created by Rayn @@ 2014/05/10
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
int a[50000], sz;

void init()
{
    for (int i = 1; (i*(i+1)/2)<=123456789;i++)
    {
        a[i] = i*(i+1)/2;
        sz = i;
    }
    sz++;
}

int main()
{
    init();
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        int k = lower_bound(a+1,a+sz,n) - a;
        int flag=0;
        if (a[k]==n)
        {
            flag = 1;
            printf("%d\n",k);
        }
        else
        {
            for (int i = 1; a[i] <= n; i++)
            {
                int l = n - a[i];
                int j = lower_bound(a+1,a+sz,l) - a;
                if (a[j]== l)
                {
                    printf("%d %d\n",i,j);
                    flag = 1;
                    break;
                }

            }
        }
        if (!flag)
        {
            for (int i = 1; a[i]<= n; i++)
            {
                for (int j = 1; a[i]+a[j]<=n && !flag;j++)
                {
                    int l = n - a[i] - a[j];
                    int k = lower_bound(a+1,a+sz,l) - a;
                    if (a[k] == l)
                    {
                        printf("%d %d %d\n",i,j,k);
                        flag = 1;
                        break;
                    }
                }
            }
        }
    }
    return 0;
}

C - Diablo III

【题意】yuzhi在玩游戏"大菠萝3",有13个武器插槽,装备如下 {"Head", "Shoulder", "Neck", "Torso", "Hand", "Wrist", "Waist", "Legs", "Feet", "Finger", "Shield", "Weapon", "Two-Handed"},"Finger"可以戴两个,还有戴了"Two-Handed"就不能戴"Shield"和"Weapon"。然后每个武器都有两个属性: Damage和Toughness,要求在Toughness不小于M的情况下,怎样选择戴装备让Damage最大。

【思路】开始一直没有读懂题,其实这题就是一个包含特例的01背包问题,每个武器可以戴一个,关键是在于"Finger"可以戴两个,所以要先对"Finger"的状态进行处理。设置dp[i][j]代表佩戴前i件装备,Toughness为j的状况下最大的Damage。还有就是"Two-Handed"的状态不能从"Shield", "Weapon"转移过来,因为这两个不能共存。

代码如下:

/*
** ZOJ 3769 Diablo III
** Created by Rayn @@ 2014/05/12
** 背包
*/
#include <cstdio>
#include <iostream>
#include <vector>
#include <map>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXM = 50000 + 10;

struct Equip {
    int D, T;
    Equip(int d = 0, int t = 0): D(d), T(t) {}
};
int dp[13][MAXM];
map<string, int> equip_id;

void Init()
{
    equip_id["Finger"] = 0;
    equip_id["Head"] = 1;
    equip_id["Shoulder"] = 2;
    equip_id["Neck"] = 3;
    equip_id["Torso"] = 4;
    equip_id["Hand"] = 5;
    equip_id["Wrist"] = 6;
    equip_id["Waist"] = 7;
    equip_id["Legs"] = 8;
    equip_id["Feet"] = 9;
    equip_id["Shield"] = 10;
    equip_id["Weapon"] = 11;
    equip_id["Two-Handed"] = 12;
}
inline void takemax(int &a, int b)
{
    if(a < b) a = b;
}
int main()
{
    int T, N, M;

    Init();
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &N, &M);

        vector<Equip> E[13];
        string s;
        for(int i=0; i<N; ++i)
        {
            int d, t;
            cin >> s >> d >> t;
            E[equip_id[s]].push_back(Equip(d,t));
        }
        E[0].push_back(Equip(0, 0));
        memset(dp, -1, sizeof(dp));
        dp[0][0] = 0;
        for(int i=0; i<(int)E[0].size(); ++i)
        {
            for(int j=0; j<i; ++j)
            {
                takemax(dp[0][min(E[0][i].T + E[0][j].T, M)], E[0][i].D + E[0][j].D);
            }
        }
        int ans = -1;
        for(int i=1; i<=12; ++i)
        {
            int p = (i == 12)? 9 : i - 1;
            memcpy(dp[i], dp[p], sizeof(dp[p]));
            for(int k=0; k<E[i].size(); ++k)
            {
                for(int j=0; j<=M; ++j)
                {
                    if(dp[p][j] < 0)
                        continue;
                    takemax(dp[i][min(j + E[i][k].T, M)], dp[p][j] + E[i][k].D);
                }
            }
            takemax(ans, dp[i][M]);
        }
        printf("%d\n", ans);
    }
    return 0;
}


D - Ranking System

【题意】给你一些人的ID,加入时间,积分。要求给这些人分配等级(LV1 ~ LV6,具体分配规则见题目)。

【思路】这道题也是一道大水题,就根据他的规则去模拟就行了,开始看了题之后就去敲,开始感觉自己写的很挫,还排了两次序,幸好最后1A了。下面是代码:

/*
** ZOJ 3770 Ranking System
** Created by Rayn @@ 2014/05/10
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX = 2005;

struct Member{
    int num;
    int ID, date, score;
    int degree;
} man[MAX];

int cmp1(const Member& A, const Member &B)
{
    if(A.score != B.score)
        return A.score < B.score;
    if(A.date != B.date)
        return A.date > B.date;
    else
        return A.ID > B.ID;
}
int cmp2(const Member& A, const Member &B)
{
    return A.num < B.num;
}
int main() {

#ifdef HotWhite
    //freopen("in.txt", "r", stdin);
#endif

    int T, n;
    scanf("%d", &T);
    while(T--)
    {
        memset(man, 0, sizeof(man));
        scanf("%d", &n);
        int cnt = 0;
        for(int i=0; i<n; ++i)
        {
            int y,m,d;
            scanf("%d %d/%d/%d %d", &man[i].ID, &y,&m,&d, &man[i].score);
            man[i].num = i+1;
            man[i].date = d + m*100 + y*10000;
            if(man[i].score > 0)
                cnt++;
        }
        sort(man, man+n, cmp1);
        /*
        for(int i=0; i<n; ++i)
        {
            printf("%d %d %d\n", man[i].ID, man[i].date, man[i].score);
        }
        //*/
        int lv[7];
        lv[1] = n - cnt;
        lv[3] = (int)(cnt * 0.3);
        lv[4] = (int)(cnt * 0.2);
        lv[5] = (int)(cnt * 0.07);
        lv[6] = (int)(cnt * 0.03);
        lv[2] = cnt-lv[3]-lv[4]-lv[5]-lv[6];
        for(int i=0, k=1; i<n; ++k)
        {
            for(int j=1; j<=lv[k]; ++j, ++i)
            {
                man[i].degree = k;
            }
        }
        sort(man, man+n, cmp2);
        for(int i=0; i<n; ++i)
        {
            printf("LV%d\n", man[i].degree);
        }
    }
    return 0;
}


E - Easy 2048

F - Calculate the Function

【题意】给你一个n个数的序列 Ai,定义一个[L,R]的查询,求Fi(R)的值。

  • F(Li) = ALi ;
  • F(Li + 1) = A(Li + 1);
  • for all x >= Li + 2F(x) = F(x – 1) + F(x – 2)  ×  Ax;

【思路】由上面的公式,对于每一个Ai可以构造出QQ截图20140513095147,则Fi(R)可以由下面的公式

QQ截图20140513095048

然后用线段树维护这个矩阵,查询就很快速了。下面是代码:

/*
** ZOJ 3772 Calculate the Function
** Created by Rayn @@ 2014/05/13
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MOD 1000000007
using namespace std;
typedef long long LL;
const int MAXN = 100005;

struct Martix
{
    LL arr[2][2];
    Martix(int x11=0, int x12=0, int x21 =0, int x22=0)
    {
        arr[0][0] = x11; arr[0][1] = x12;
        arr[1][0] = x21; arr[1][1] = x22;
    }
    Martix operator * (const Martix& b) const
    {
        Martix res(0, 0, 0, 0);
        for(int i=0; i<2; ++i)
        {
            for(int j=0; j<2; ++j)
            {
                for(int k=0; k<2; ++k)
                {
                    res.arr[i][j] += arr[i][k] * b.arr[k][j];
                    res.arr[i][j] %= MOD;
                }
            }
        }
        return res;
    }
};
struct SegmentTree
{
    int l, r;
    Martix val;
} tree[MAXN<<2];

int A[MAXN];

void BuildTree(int l, int r, int root)
{
    tree[root].l = l;
    tree[root].r = r;
    if(l == r)
    {
        tree[root].val = Martix(1, 1, A[l], 0);
        return ;
    }
    int mid = (l + r) >> 1;
    BuildTree(l, mid, root<<1);
    BuildTree(mid+1, r, root<<1|1);
    tree[root].val = tree[root<<1].val * tree[root<<1|1].val;
}
Martix Query(int left, int right, int root)
{
    if(left <= tree[root].l && tree[root].r <= right)
    {
        return tree[root].val;
    }
    Martix res(1, 0, 0, 1);
    int mid = (tree[root].l + tree[root].r) >> 1;
    if(left <= mid)
        res = res * Query(left, right, root<<1);
    if(right > mid)
        res = res * Query(left, right, root<<1|1);
    return res;
}
int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        int N, M;
        scanf("%d%d", &N, &M);
        for(int i=1; i<=N; ++i)
        {
            scanf("%d", &A[i]);
        }
        BuildTree(1, N, 1);
        while(M--)
        {
            int L, R;
            scanf("%d%d", &L, &R);
            if(R - L <= 1)
            {
                printf("%d\n", A[R]);
            }
            else
            {
                Martix res = Query(L+2, R, 1);
                LL ans = (A[L+1]*res.arr[0][0] + A[L]*res.arr[1][0]) % MOD;
                printf("%lld\n", ans);
            }
        }

    }
    return 0;
}


G - Paint the Grid

H - Power of Fibonacci

I - ?(>_o)!

【题意】?(>_o)! 是一种简单的语言,给你一段源代码,然后判断输出是否和源代码相同。

【思路】这道题说了很多废话,其实就是就两个地方需要判断,对于输出,存在"_"会输出源代码,"!"会输出"Hello, world!"。然后就判断一下是否和源代码相同即可。代码如下:

/*
** ZOJ 3775 ?(>_o)!
** Created by Rayn @@ 2014/05/10
*/
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
using namespace std;

bool Check(string& s)
{
    string tmp = "";
    for(int i=0; i<s.length(); ++i)
    {
        if(s[i] == '_')
            tmp += s;
        else if(s[i] == '!')
            tmp += "Hello, world!";
        if(tmp.length() > s.length())
            return false;
    }
    return s == tmp;
}
int main()
{
    int t;
    string str;

    scanf("%d%*c", &t);
    while(t--)
    {
        getline(cin, str);
        if(Check(str))
            puts("Yes");
        else
            puts("No");
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值