2022 牛客多校2 L,5 A(分层图dp,对边做dp)

2022 牛客多校2 L,5 A(对边dp,分层图dp)

题意

\(n\) 个点,和原点,原点到其他点单向,其他点之间可互达。边权是两点间的距离。

要求走到下一个点的距离严格小于上一次移动距离。求走的最大步数。

思路

乍一看因为可以成环无法dp。但仔细想一想可以发现每条边至多使用一次。所以对边dp是没有后效性的。

考虑 \(f[i]\) 表示最后走到 \(i\) 的最大步数。一开始我们位于 \(0\) ,初始状态为 \(f[0]=0\) 其余状态非法,设为无穷小。

考虑转移,我们令 \(g[i]\) 表示下一个状态。

如果有一条 \((u,v)\) 的边,会进行 \(g[v] = max(g[v],f[u]+1)\) 的转移。

因为要保证每次走的边比上一步短,因此事先要对边排序。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<functional>
#include<random>
#include<iomanip>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
using PII = array<int,2>;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;

void solve()
{    
    int n; cin >> n;
    vector<PII>a(n + 1);
    for(int i = 1;i <= n;i += 1)
        cin >> a[i][0] >> a[i][1];
    
    auto getDis2 = [&](int x,int y) {
        int dx = a[x][0] - a[y][0];
        int dy = a[x][1] - a[y][1];
        return dx * dx + dy * dy;
    };

    vector<array<int,3>> e;
    for(int i = 0;i <= n;i += 1) {
        for(int j = 1;j <= n;j += 1) {
            if(i == j) continue;
            e.push_back({getDis2(i,j),i,j});
        }
    }

    sort(e.begin(),e.end());
    reverse(e.begin(),e.end());

    int m = e.size();
    vector<int> f(n + 1,-linf),g(n + 1,-linf);
    f[0] = 0;
    for(int i = 0;i < m;i += 1) {
        int j = i;
        vector<int> s;
        while(j < m and e[i][0] == e[j][0]) {
            auto [_,u,v] = e[j];
            g[v] = max(g[v],f[u] + 1);
            s.push_back(v);
            j += 1;
        }
        for(auto v : s)
            f[v] = g[v];
        i = j - 1;
    }

    cout << *max_element(f.begin(),f.end());
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

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

    return 0;
}

这个dp和前两场的分层图dp很像,

L-Link with Level Editor I_"蔚来杯"2022牛客暑期多校训练营2 (nowcoder.com)

题意

林克正在玩一个游戏,叫做NIO的游戏。

在这个游戏中,一个关卡由几个世界组成。每个世界由 \(m\) 个节点和一些定向道路组成。玩家从第一个世界的第 \(1\) 个节点开始。在每个世界中,玩家可以停留在当前的节点上,或者正好通过该世界中存在的一条道路。之后,玩家将被传送到下一个世界,而不改变他所停留的节点的 \(ID\)。如果没有下一个世界,游戏结束。如果玩家在节点 \(m\) 上结束,他就赢了。

林克正在编辑一个新的关卡,他已经做了 \(n\) 个世界,想选择其中一个连续的子段来组成一个新的关卡。唯一的限制是,至少要有一种获胜的方式

林克不想使用太多的世界。在新的关卡中,林克需要使用的最少世界数是多少?

思路

如果我们把走向下一个世界认为是 “距离” 变大。翻译一下就是在一个图上,每次我们 走的距离比上一次大 。求最小步数。

此时几乎和上面那个题一样了。我们只需要对边dp,定义 \(f[i]\) 表示最后停在 \(i\) 上的最小步数dp就行了。


可以发现,该类dp问题的特征是上一步和下一步满足一个大小关系,此时我们要做的就是 按序 的处理状态。可以把它理解为在一个分层图上进行dp。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<random>
#include<iomanip>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
// #define int long long
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;

void solve()
{    
    int n,m; cin >> n >> m;
    vector<int> dp(m + 1,0);
    int ans = inf;
    for(int i = 1;i <= n;i += 1) {
        auto ndp = dp;
        ndp[1] = i;
        int l; cin >> l;
        for(int j = 0;j < l;j += 1) {
            int u,v; cin >> u >> v;
            dp[v] = max(dp[v],ndp[u]);
        }
        if(dp[m]) ans = min(ans, i - dp[m] + 1);
    }
    cout << (ans == inf ? -1 : ans);
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

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

    return 0;
}
posted @ 2022-08-02 20:30  Mxrurush  阅读(52)  评论(5编辑  收藏  举报