上海市中小学生人工智能算法设计复赛 - 高中组 (S) 比赛游记

比赛链接(已结束):戳我打开

比赛时间:15:30~19:00。

估了好久的补偿赛,感觉题目质量还行吧。

第一题:

看到之后居然感觉无从下手?

有了最小的数据范围 m10 之后,想到枚举每个人是否 Hack

打算写之前随便特判了几组数据,然后发现有人已经 A 了?

想写的念头就没了,接着发现 T107,算法应该是 Tlogm 的样子吧。

然后还是没什么思路,写了个动态规划。

f[i] 表示花 i 个时间做题能拿到的最大分数,就是个 0/1 背包。

感觉写出来之后是否 Hack 就出来了。

结果发现真的出来了,直接枚举花多长时间做题,剩下的时间 Hack 就行。

但是花这么多时间 Hack 能得到多少的排名呢?

还是需要一个动态规划, f[i][j] 表示前 i 名中 Hack j 名的最少时间花费。

状态转移方程为 f[i][j]=min(f[i1][j],f[i1][j1]+t[i])

二分就行了,时间复杂度为 O(t×n+T×logm),提交发现直接 A 了。

A 了之后才知道会爆啊,洛谷机子是真的快,n 方过百万。

考试结束后听同学说应该把分数作为维度算,反正过了就行了呗(\doge。

考场代码:

复制代码
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n, m, _t, ans = 0x3f3f3f3f;
int f[10000005], dp[505][505];//f[i]:花费i时间做题所能得到的最大分数
//dp[i][j]:前i个人中Hackj个人所需要花费的最少时间
struct problem {
    int t, s;
}a[110];
struct people {
    int t, s;
}b[510];
bool cmp (people p1, people p2) {
    return p1.s > p2.s || p1.s == p2.s && p1.t < p2.t;
}
int main () {
    scanf ("%d%d%d", &n, &m, &_t);
    for (int i = 1; i <= n; i ++) scanf ("%d%d", &a[i].t, &a[i].s);
    for (int i = 1; i <= m; i ++) scanf ("%d%d", &b[i].t, &b[i].s);
    sort (b + 1, b + m + 1, cmp);
    for (int i = 1; i <= n; i ++) for (int j = _t; j >= 0; j --) if (j >= a[i].t) f[j] = max(f[j], f[j - a[i].t] + a[i].s);
    memset (dp, 0x3f, sizeof dp);
    dp[1][0] = 0;
    dp[1][1] = b[1].t;
    for (int i = 2; i <= m; i ++) {
        dp[i][0] = 0;
        for (int j = 1; j <= i; j ++)
            dp[i][j] = min(dp[i - 1][j], dp[i - 1][j - 1] + b[i].t);
    }
    for (int i = 0; i <= _t; i ++) {//枚举花费多长的时间做题
        int score = f[i], rank;//所能够得到的分数
        int l = 1, r = m, mid;//二分查找该分数的排名 
        while (l <= r) {
            mid = l + r >> 1;
            if (b[mid].s > score) l = mid + 1;
            else r = mid - 1;
        }
        rank = r + 1;//当前这个人的排名
        int t_ = _t - i;//剩下的可以Hack别人的时间
        l = 1;
        r = rank - 1;//二分查找最多能Hack前rank名中的几名
        while (l <= r) {
            mid = l + r >> 1;
            if (dp[rank - 1][mid] > t_) r = mid - 1;//需要更多的时间,无法Hack 
            else l = mid + 1;//可以Hack,扩大左边界 
        }
        ans = min (ans, rank - r);//可以Hack r名,排名由rank名变为rank-r名 
    }
    printf ("%d", ans);
    return 0;
}
复制代码

 

第二题:

方格路径加强版,考前没复习,打了 n10540 分暴力就没管了。

状态转移方程:f[1][i]=f[1][i1]+f[2][i1]

f[i][j]=f[i1][j1]+f[i][j1]+f[i+1][j1](2i5)

f[6][i]=f[5][i1]+f[6][i1]]

如果有障碍物,f[i][j]=0

后来看到有些人拿了 60 分,以为拿的是障碍物一格的那个。

仔细一看数据范围有个 n1018m0,就是个递推啊。

写了个矩阵乘法 63×logn 考试结束前 12 分钟拿了 60 分。

考场 60 分代码:

复制代码
#include <iostream>
using namespace std;
const int mod = 1e9 + 7;
long long n, m;
long long x1, y1, x2, y2;
long long f[7][100005], ans[7][7], pre[7][7] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1};
bool a[7][100010];
void time (long long a[7][7], long long b[7][7]) {
    long long c[7][7] = {0};
    for (int i = 1; i <= 6; i ++)
        for (int k = 1; k <= 6; k ++)
            for (int j = 1; j <= 6; j ++)
                c[i][k] = (c[i][k] + a[i][j] * b[j][k] % mod) % mod;
    for (int i = 1; i <= 6; i ++)
        for (int j = 1; j <= 6; j ++) a[i][j] = c[i][j];
}
int main () {
    cin >> n >> m;
    if (m == 0) {
        if (n == 1) {
            cout << 6;
            return 0;
        }
        else if (n == 2) {
            cout << 16;
            return 0;
        }
        for (int i = 1; i <= 6; i ++)
            for (int j = 1; j <= 6; j ++) ans[i][j] = 1;
        m = n - 2;
        while (m) {
            if (m & 1) time (ans, pre);
            time (pre, pre);
            m >>= 1;
        }
        cout << (ans[1][1] + ans[1][2] + ans[2][1] + ans[2][2] + ans[2][3] + ans[3][2] + ans[3][3] + ans[3][4] + ans[4][3] + ans[4][4] + ans[4][5] + ans[5][4] + ans[5][5] + ans[5][6] + ans[6][5] + ans[6][6]) % mod;
    }
    else {
        for (int i = 1; i <= m; i ++) {
            cin >> x1 >> y1 >> x2 >> y2;
            for (long long j = x1; j <= x2; j ++)
                for (long long k = y1; k <= y2; k ++)
                    a[j][k] = true;
        }
        f[1][1] = f[2][1] = f[3][1] = f[4][1] = f[5][1] = f[6][1] = 1;
        for (int j = 2; j <= n; j ++) {
            if (! a[1][j]) f[1][j] = (f[1][j - 1] + f[2][j - 1]) % 1000000007;
            for (int i = 2; i <= 5; i ++)
                if (! a[i][j]) f[i][j] = (f[i - 1][j -  1] + f[i][j - 1] + f[i + 1][j - 1]) % 1000000007;
            if (! a[6][j]) f[6][j] = (f[5][j - 1] + f[6][j - 1]) % 1000000007;
        }
        cout << ( ( (f[1][n] + f[2][n]) % 1000000007 + (f[3][n] + f[4][n]) % 1000000007) % 1000000007 + (f[5][n] + f[6][n]) % 1000000007) % 1000000007;
    }
    return 0;
}
复制代码

 

第三题:

毒瘤题,考试的时候以为是结论题,写了不对。

以为是二分题,写一大会儿不出。

然后写 220 爆搜枚举每一次左拐还是右拐还 WA 了。

发现 check 没查完整地图,查了 94,改成 104 之后 T 了?

就加了一个”随机性剪枝“,反正合法方案肯定很多,如果 rand()return  掉。

有一个点正好 999ms 卡过,于是就得了 10 分。

第四题:

我打 2OI 总结出来的规律,题目背景很简单的又很难打的就是难题。

于是打了 5 分暴力,后来发现可以打 O(m×n) 的算法,就求个交集。

然后打出来就 15 分了。

总共:100+60+10+15=185 分,rank 44,还可以吧。

wjh(Graygoo):100+100+0+10=210 分,rank 29,%%%

mxt(MrImposter):20+100+0+0=120 分,rank 107

yzf(Yang818):60+40+0+15=115 分,rank 111

wzy(UFO2007):60+40+0+5=105 分,rank 117

posted @   Xy_top  阅读(508)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示