Codeforces Round #776 (Div. 3)

Codeforces Round #776 (Div. 3)

E

一个求划分段长度最小值最大化的问题。

题解写的贪心。

但我用二分写的

枚举最小距离,判断是否合法

合法性判断细节挺多的,肯定要移动距离最短的

在距离最短的两考试中,移动左边或者右边得分类(特判第一个)

同时放置位置在最后一门考试之后也得特判。

非常的恶心,代码也写的很丑

#include<vector>
#include<iostream>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define lowbit(x) x&-x
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;

void solve()
{    
    int N,D;
    cin >> N >> D;
    vector<int> a(N + 2);
    for(int i = 1;i <= N;i ++) {
        cin >> a[i];
    }
    a[0] = 0;
    a[N + 1] = D + 1;
    sort(a.begin(),a.end());

    auto chk = [&](int mid) {
        int id = 0;
        for(int i = 1;i <= N;i ++) {
            if(a[i] - a[i - 1] - 1 < mid) { // 找不满足要求的
                id = i;
                break;
            }
        }

        if(id == 0) return true;// 没有返回 true

        vector<int> t;
        for(int i = 0;i <= N + 1;i ++) {
            if(i != id) t.push_back(a[i]);
        }
        bool ok = 0;
        for(int i = 1;i < t.size();i ++) { // 移动右边的
            if(((t[i] - t[i - 1] - 2 ) / 2) >= mid) {
                ok = 1;
            }
            if(i + 1 == t.size()) {// 特判放最后
                if((D - t[i - 1] - 1)  >= mid) {
                    ok = 1;
                }
            }
            if((t[i] - t[i - 1] - 1 ) < mid && i + 1 != t.size()) {
                ok = 0;
                break;
            }
        }



        if(ok) {
            return true;
        }

        if(id - 1 == 0) {// 特判最小段右边是第一个
            return ok;
        }

        vector<int> tt;
        for(int i = 0;i <= N + 1;i ++) {
            if(i != id - 1) tt.push_back(a[i]);
        }

        for(int i = 1;i < tt.size();i ++) {
            if((tt[i] - tt[i - 1] - 2) / 2 >= mid) {
                ok = 1;
            }
            if(i + 1 == tt.size()) {
                if((D - tt[i - 1] - 1)  >= mid) {
                    ok = 1;
                }
            }
            if((tt[i] - tt[i - 1] - 1) < mid && i + 1 != tt.size()) {
                ok = 0;
                break;
            }
        }

        return ok;
    };

    int l = -1,r = D + 1;
    while(l + 1 < r) {
        int mid = l + r >> 1;
        if(chk(mid)) l = mid;
        else r = mid;
    }

    cout << l << endl;
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);

    int T;cin>>T;
    while(T--)
        solve();

    return 0;
}

F

记录方案的01背包

但是还是代码实现起来有点恶心,还是coding能力不行

题中每任务都是独立的,所以对每一个任务按DDL顺序跑01背包就行

dp中记录的是完成任务的最短时间

跑完要记录方案

方法就是从后往前回溯,当dp值发生改变时,一定使用了当前决策

此时找出正确的状态转移,然后回溯下一次决策。

注意记录时间花费,判断是否超过DDL

代码参考jiangly,非常简洁优雅。

#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define debug(x) cout<<'>' << ' ' << x<<endl;
#define ull unsigned long long
#define endl '\n'
#define lowbit(x) x&-x
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
int N,M;
int ddl[MAXN];
struct Node {
    int t,p,id;
};
void solve()
{    
    cin >> N >> M;
    for(int i = 0;i < N;i ++) {
        cin >> ddl[i];
    }
    vector<vector<Node>> a(N);
    for(int i = 0;i < M;i ++) {
        int t,p,e;
        cin >> e >> t >> p;
        a[-- e].push_back({t,p,i});
    }

    vector<int> ans;
    int cur = 0;

    for(int i = 0;i < N;i ++) {
        int n = a[i].size();
        vector<vector<int>> dp(n + 1,vector<int>(101,linf));
        dp[0][0] = 0;
        
        for(int j = 0;j < n;j ++) {
            auto [t,p,id] = a[i][j];
            dp[j + 1] = dp[j];
            for(int k = 0;k <= 100;k ++) {
                int nk = min(100ll,p + k);
                dp[j + 1][nk] = min(dp[j + 1][nk],dp[j][k] + t);
            }
        }
        cur += dp[n][100];
        if(cur > ddl[i]) {
            cout << -1 << endl;
            return;
        }
        
        int ed = 100;
        for(int j = n - 1;j >= 0;j --) {
            auto [t,p,id] = a[i][j];
            if(dp[j + 1][ed] == dp[j][ed]) {
                continue;
            }
            
            ans.push_back(id);
            for(int k = 0;k <= 100;k ++) {
                if(min(100ll,k + p) == ed && dp[j + 1][ed] == dp[j][k] + t) {
                    ed = k;
                    break;
                } 
            }
        }
    }

    cout << ans.size() << endl;
    for(int x : ans) {
        cout << x + 1 << " ";
    }
    cout << endl;
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);

    int T;cin>>T;
    while(T--)
        solve();

    return 0;
}

G

题目中虽然不保证路径是简单路径,但因为要算的是比最短路大1的次短路,图中又没有自环重边,如果跑环或者重复走一条路,长度最少加2,所以一定求的是简单路径。

然后这就是个次短路计数板子了

虽然是板子,但因为是第一次写,还有很多没想明白

本题因为是无权图,所以其实有更简单的BFS写法

可惜还不会(

迪杰斯特拉写法就是记录最短路和次短路的长度与数目

在跑迪杰斯特拉的过程中,如果满足

\[dis[v][0] > dis[u][flg] + w \]

正常就会进行松弛操作,更新最短路

但是如果加入对次短路的考虑,可以发现这个还未更新的最短路在一次更新后就会变成新的次短路,并将之前的次短路给覆盖

因此在进行正常的最短路松弛之前,还要做次短路的记录

\[dis[v][1] = dis[v][0] \]

\[cnt[v][1] = cnt[v][0] \]

类似的

当发现有新的路径比当前的次短路还要小且比最短路大,这个新的次短路就要覆盖掉老的次短路

\[dis[v][1] = dis[u][flg] + w \]

\[cont[v][1] = cnt[u][flg] \]

当我们对次短路和最短路的状态进行更新后,要将这个结点入队。

最后答案就是最短路数量加上比比最短路长1的次短路数量

#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define debug(x) cout<<'>' << ' ' << x<<endl;
#define ull unsigned long long
#define endl '\n'
#define lowbit(x) x&-x
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
int N,M;
vector<vector<PII>> G;
struct Node {
    int u,flg,d;
    bool operator>(const Node& x) const {
        return d > x.d;
    }
};
int dij(int s,int f) {
    vector< array<int,2> > cnt(N,{0,0}),dis(N,{linf,linf});
    dis[s][0] = 0;
    cnt[s][0] = 1;
    
    priority_queue<Node,vector<Node> ,greater<Node> > q;
    q.push({s,0,0});

    vector< array<bool,2> > st(N,{0,0});
    while(q.size()) {
        auto [u,flg,d] = q.top();
        q.pop();

        if(st[u][flg]) continue;
        st[u][flg] = 1;

        for(auto [v,w] : G[u]) {
            if(dis[v][0] > dis[u][flg] + w) {
                dis[v][1] = dis[v][0];
                cnt[v][1] = cnt[v][0];
                q.push({v,1,dis[v][1]});

                dis[v][0] = dis[u][flg] + w;
                cnt[v][0] = cnt[u][flg];
                q.push({v,0,dis[v][0]});
            }else if(dis[v][0] == dis[u][flg] + w) {
                cnt[v][0] += cnt[u][flg];
                cnt[v][0] %= mod;
            }else if(dis[v][1] > dis[u][flg] + w) {
                dis[v][1] = dis[u][flg] + w;
                cnt[v][1] = cnt[u][flg];
                q.push({v,1,dis[v][1]});
            }else if(dis[v][1] == dis[u][flg] + w) {
                cnt[v][1] += cnt[u][flg];
                cnt[v][1] %= mod;
            }
        }
    }

    int res = cnt[f][0];
    if(dis[f][0] + 1 == dis[f][1]) (res += cnt[f][1]) %= mod;
    return res;
}
void solve()
{    
    cin >> N >> M;
    G.clear();
    G.resize(N);
    int s,f;
    cin >> s >> f;
    s -- ,f --;
    for(int i = 0;i < M;i ++) {
        int u,v;
        int w = 1;
        cin >> u >> v;
        u --,v --;
        G[u].push_back({v,w});
        G[v].push_back({u,w});
    }  

    cout << dij(s,f) << endl;
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);

    int T;cin>>T;
    while(T--)
        solve();

    return 0;
}
posted @ 2022-03-16 19:53  Mxrurush  阅读(71)  评论(0)    收藏  举报