第46届ICPC 澳门 热身赛 + 正式赛(F/K/A)

这篇博客主要探讨了几道编程竞赛中的题目,包括热身赛问题的解决策略,如寻找最长序列、处理循环和路径优化。作者通过分析代码和样例,解释了如何理解和解决问题,涉及动态规划、图论和字符串处理等多个算法知识点。此外,博客还提到了一些常见的错误和优化技巧,如正确使用数据结构、避免段错误和理解题目要求的重要性。
摘要由CSDN通过智能技术生成

热身赛
A.一定不会产生冲突,所以从1开始必定是最长的

bool vis[maxn];//这个位置标记取了

int main() {
    IOS;
    int n, k;
    cin >> n >> k;
    for (int i = 1; i <= n; ++i){
        ll j = i;
        while(j <= n &&!vis[j]){//注意这里一定要先判j的大小,否则会段错误
            cout << j << " ";
            vis[j] = true;
            j += k;
        }
    }
        return 0;
}

B.
太难受了,自己写的一直找不出错误。
以后这么写吧…

需要记住的是最好在一开始就处理全局的;
再碰到 %n 的从1开始。

int val[maxn];//已ac
map<int, int> mp;
int sat[maxn];

int main() {
    IOS;
    int t;
    cin >> t;
    while(t--){
        re(sat);
        re(val);
        mp.clear();
        int n, k, m;
        cin >> n >> k >> m;
        for (int i = 1; i <= n; ++i){
            cin >> val[i];
            ++mp[val[i]];//记下是奇数个
            sat[i] = mp[val[i]];//赋给这一位
        }
        int kk = m % n;
        for(int i = 1; i <= n; ++i){
            int te = m / n;
            if(m % n >= i)
                ++te;
            if(mp[val[i]] % 2 == 0){
                if(sat[i] % 2 != 0){
                    te = 0;
                }
            }
            else {
                if(sat[i] % 2 == 0)
                   te = (te + 1) / 2;//啊这是为什么啊
                   //懂了,要看这个数的位数和这个次数能不能对应
                else
                    te = te / 2;
            }
            if(i == 1)
                cout << te;
            else
                cout << " " << te;
        }
        cout << endl;
    }
        return 0;
}

还是第二种方法好懂。

分析了下,这个分类更细。
以后加不加可以写成 t += (n >= i)
int main() {
    IOS;
    int t;
    cin >> t;
    while(t--){
        re(sat);
        re(val);
        mp.clear();
        int n, k, m;
        cin >> n >> k >> m;
        for (int i = 1; i <= n; ++i){
            cin >> val[i];
            ++mp[val[i]];//记下是奇数个
            sat[i] = mp[val[i]];//赋给这一位
        }

        int na = m / n, nb = m % n;
        for(int i = 1; i <= n; ++i){
            int te = 0;
            if(mp[val[i]] % 2 == 0){
                if(sat[i] % 2)
                    te = 0;
                else
                    te = na + (nb >= i);
            }
            else {
                te = na / 2;
                if(na % 2 == 0){
                    if(sat[i] % 2 == 0)
                        te += (nb >= i);
                }
                else {
                    if(sat[i] % 2)//一共奇个、奇数次、奇数位
                        te += (nb >= i);
                    else
                        ++te;
                }
            }
            if (i == 1)
                cout << te;
            else
                cout << " " << te;
        }
        if(t >= 1)
            cout << endl;
    }
    return 0;
}

C.
瞎推的,数据范围决定了显然不可能是一个一个试。
开始推的是只由最后一次决定,中间就算是负数也是越走越远。
50%wa后猜测第一次也许会更远,判了第一次后就过了

#define PII pair<int, int>

PII R = {1, 0}, U = {0, 1}, D = {0, -1}, L = {-1, 0};

int main() {
    IOS;
    int t;
    cin >> t;
    while(t--){
        ll n, k;
        cin >> n >> k;
        string s;
        cin >> s;
        //我悟了,这个过程中的最大值
        //只需要算最后一次的
        ll x = 0, y = 0;
        ll ans = abs(x) + abs(y);
        for (int i = 0; i < n; ++i){
            if(s[i] == 'R'){
                x += R.first;
                y += R.second;
            }
            else if(s[i] == 'U'){
                x += U.first;
                y += U.second;
            }
            else if(s[i] == 'D'){
                x += D.first;
                y += D.second;
            }
            else if(s[i] == 'L'){
                x += L.first;
                y += L.second;
            }
            ans = max(ans, abs(x) + abs(y));
        }
        x = (1ll) * x * (k - 1);
        y = (1ll) * y * (k - 1);
        ans = max(ans, abs(x) + abs(y));
        for (int i = 0; i < n; ++i){
             if(s[i] == 'R'){
                x += R.first;
                y += R.second;
            }
            else if(s[i] == 'U'){
                x += U.first;
                y += U.second;
            }
            else if(s[i] == 'D'){
                x += D.first;
                y += D.second;
            }
            else if(s[i] == 'L'){
                x += L.first;
                y += L.second;
            }
            ans = max(ans, abs(x) + abs(y));
        }
        cout << ans << endl;
    }
    return 0;
}

F.

题意又读假了,神奇的是看样例解释居然和读假的意思一样…
又去读了一次题,这题好难读懂绝对不是我英语差
啊不会
这个是最强最简洁的代码,很漂亮,直接找出了本质
这个挺容易理解的,就是判后模拟

知识点:
一个环如果分给其余的点,可推出最多n-1次不陷入死循环,大于n-1次即循环
或者说,无限循环时,有一个点被分成n-1份,有一个点被分成n-2份……那个n-1的点又能再被分为n-1(例3 2 1)
像有判 sum > n * (n - 2) 时就输出 Recurrent (大于n个n-2构成循环)
是因为最小的不会循环的数是 1 1 0,而像1 1 1,2 1 0等都会产生循环
优先队列可以自动排序,默认最大的永远在前面

他这个代码是判断出了最多n-1次,然后模拟,从大局出发的。
一直减n个(好算最终代价,直接加,不需要再判了)
这是一种好想法
#define re(a) memset((a), 0, sizeof((a)))
#define remax(a) memset((a), 0x3f3f3f3f, sizeof((a)))
#define PII pair<int, int>

int a[maxn];

int main() {
	IOS;
    int n;
    cin >> n;
    int sum = 0;
    priority_queue<PII> q;
    for (int i = 1; i <= n; ++i){
        cin >> a[i];
        sum += a[i];
        q.push({a[i], i});
    }
    //if(sum > n * (n - 2)) {加了这个反而是错的,离谱哈,到时候再会过来看会错在哪。离谱,是爆int了,改成ll就过了
   //     cout << "Recurrent" << endl;
   //     return 0;
   // }
    for (int i = 0; i < n - 1; ++i){
        PII temp = q.top();
        q.pop();
        if(temp.first + i < n - 1){
            for (int j = 1; j <= n; ++j){
                if(j == 1)
                    cout << a[j] + i;
                else
                    cout << " " << a[j] + i;
            }
            cout << endl;
            return 0;
        }
        a[temp.second] -= n;
        q.push({a[temp.second], temp.second});
    }
    cout << "Recurrent" << endl;
        return 0;
}

K.

澳门题好难,我好菜
看不懂样例;

下次如果在确认题没读错的情况下,如果思路对不上样例,很可能是题目含了什么算法,往怎么来的去考虑;如果这道题很多人做出来,可以考虑先硬莽 我判断错了变量,原来输出的是第几条边啊
Otherwise output k integers separated by a space in increasing order indicating the indices of the edges in the simple cycle with the smallest length.

这题有bfs和dfs两种输出方法,很不想写dfs,但是吧,基本题解看过去全是dfs;就题解而言,dfs的输出思路看得 也更清晰(虽然其实思路差不多)
BFS

判断有没有成环,看要连接的点是不是在同一个连通块,如果是,那么就已经成环了,很好证明

归根到底,还是对知识掌握得不熟悉;还有老是会跳着读!!!
照着标答也能错,麻了,v.clear()和v.pop_back()是不同的,会出错
DFS1
DFS2

#define re(a) memset((a), 0, sizeof((a)))
#define remax(a) memset((a), 0x3f3f3f3f, sizeof((a)))
#define PII pair<int, int>

int n, m;
PII e[maxn];
vector<PII> v[maxn];
int pre[maxn];
vector<int> ans;
bool vis[maxn];

void init(){
    for (int i = 1; i <= n; ++i){
        pre[i] = i;
        v[i].clear();
    }
    re(e);
    re(vis);
}

int find(int x){
    if(pre[x] == x) return x;
    return pre[x] = find(pre[x]);
}

bool dfs(int st, int en, int w){
    if(st == en)
        return true;
    for(auto k : v[st]){
        if(vis[k.first] || k.second >= w)
            continue;
        vis[k.first] = true;
        if(dfs(k.first, en, w)){
            ans.push_back(k.second);
            return true;
        }
    }
    return false;
}

int main() {
	IOS;
    int t;
    cin >> t;
    while(t--){
        cin >> n >> m;
         init();//这里的位置很重要,已经在这里错好几次了
        for (int i = 1; i <= m; ++i){
            int a, b;
            cin >> a >> b;
            e[i] = {a, b};
            v[a].push_back({b, i});
            v[b].push_back({a, i});
        }
        bool flag = true;
        for (int i = 1; i <= m; ++i){
            int u = e[i].first, v = e[i].second;
            int x = find(u), y = find(v);
            if(x == y){
                ans.clear();
                vis[u] = true;
                dfs(u, v, i);
                ans.push_back(i);
                sort(ans.begin(), ans.end());
                for(auto k : ans){
                    cout << k << " ";
                }
                cout << endl;
                flag = false;
                break;
            }
            else
                pre[x] = y;
        }
        if(flag)
            cout << -1 << endl;
    }
    return 0;
}

A.

这题很翻译了几次。
翻译的方法(读不懂下):一个句子一个句子翻,写在纸上,知道每个句子的意思
n行m列值为h
走过每个格子一次
上坡比下坡少
走相邻格
输出路径

实际上又少读了句子…
题解好简单,一读就会了。只要上比下小就行,大了就反过来

#define PII pair<int, int>

int g[1100][1100];
bool vis[1100][1100];
int dx[] = {0, 1, 0, -1};
int dy[] = {1, 0, -1, 0};

void init(){
    re(g);
}

void solve(){
    int n;
    cin >> n;
    init();
    int cnt = 0;
    for (int i = 1; i <= n; ++i){
        for (int j = 1; j <= n; ++j){
            cin >> g[i][j];
        }
    }
    vector<int> ans;
    int pre = -1;
    int cnt_up = 0, cnt_down = 0;
    for (int i = 1; i <= n; ++i){
        if(i & 1){
            //直接顺着走
            for (int j = 1; j <= n; ++j){
                if(pre != -1){
                    if(pre > g[i][j])
                        ++cnt_down;
                    else
                        ++cnt_up;
                }
                ans.push_back(g[i][j]);
                pre = g[i][j];
            }
        }
        else {
            for (int j = n; j >= 1; --j){
                if(pre != -1){
                    if(pre > g[i][j])
                        ++cnt_down;
                    else
                        ++cnt_up;
                }
                ans.push_back(g[i][j]);
                pre = g[i][j];
            }
        }
    }
    if(cnt_down < cnt_up){
        reverse(ans.begin(), ans.end());
    }
    for(auto w : ans){
        cout << w << " ";
    }
}

int main() {
	IOS;
	// freopen("P1908_6.in","r",stdin);//读入数据
	//freopen("P1908.out","w",stdout); //输出数据
	int t;
    cin >> t;
    while(t--){
        solve();
        cout << endl;
    }
    return 0;
}

C.https://ac.nowcoder.com/acm/contest/31454/C
感觉挺简单的,学了再说吧
E.大意了,读题感觉挺简单的,交的人数也那么多
不愧是银牌题哈
涉及到了循环卷积、分块
佬的题解

//这过不了的,纪念一下
int p[maxn];
int mp[maxn];
int qq[maxn];
int qiu[maxn];
int res[maxn];

int main() {
	IOS;
	// freopen("P1908_6.in","r",stdin);//读入数据
	// freopen("P1908.out","w",stdout); //输出数据
    int n, q;
    cin >> n >> q;
    int ans = 0;
    for (int i = 1; i <= n; ++i){
        cin >> p[i];
        qiu[p[i]] = i;
        ans += (p[i] * i);
    }
    bool flag = false;
    int cnt = 1;
    res[0] = ans;
    ans = 0;
    for (int i = 1; i <= n; ++i){
        ans += (qiu[i] * i);
        //cout << qiu[i] << " ";
    }
    //cout << endl;
    res[1] = ans;
    while (!flag)
    {
        ++cnt;
        int temp[maxn];
        memcpy(temp, qiu, sizeof(qiu));
        for (int i = 1; i <= n; ++i)
        {
            temp[p[i]] = qiu[i];
        }
        // for (int i = 1; i <= n; ++i)
        // {
        //     cout << temp[i] << " ";
        // }
        // cout << endl;
        ans = 0;
        for (int i = 1; i <= n; ++i)
        {
            ans += (temp[i] * i);
        }
        res[cnt] = ans;
        memcpy(qiu, temp, sizeof(qiu));
        int i;
        for (i = 1; i <= n; ++i)
        {
            if (qiu[i] != i)
                break;
        }
        if (i >= n)
            flag = true;
        }
    for (int i = 1; i <= q; ++i){
        int k;
        cin >> k;
        //直接处理循环节
        cout << res[k % (n + 1)] << endl;
    }
        return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值