周测题解8.23

这个国王的新连麦键就nm尴尬

还有T3正解是暴搜T4是模拟,这里主要说一下T1和T2。

T1

一个树形DP。

设计状态

dp[i][j] 表示在以 i 为根的子树上取 j 个点的最大收益。

这里就会发现,这其实是一个森林:所有没有先修课的课都是一棵树的根。

那么我们就可以直接让输入的 0 作为这些“根”的父节点,而且 0 点是必学的(否则没办法学其他的科)。

所以一共要学 m+1 门课,即答案为 dp[0][m+1]

初始状态

这个也不难想。从 i 点为根的子树上取一个点,收益就是 si

dp[i][1]=si(1in)

状态转移

一棵树的状态从哪来?当然是从子树来了。下面称 x 的一个子节点为 v

则有状态转移方程:dp[x][i]=max(dp[x][i],dp[v][j]+dp[x][ij])(1xn,1im+1,1j<i)

这里有两个关键:

  1. j 的范围

首先 j1 应该很好理解:dp[v][0]+dp[x][i0]=dp[x][i],原状态,转移了个寂寞。

然后,j<i,如果 j=i,在子树上取了 i 个,连原树的根都没取,子树是怎么取的?显然不合理。

  1. i 的枚举顺序

这其实类似01背包。

如果 dp[x][ij] 包括 dp[v][j],那就重复包含了(看方程 max 的第二个参数)。

所以要求更新 dp[x][i] 时,dp[x][ij] 没有更新。

即大的比小的早更新,i 要倒序枚举。

代码

#include <iostream>
#include <list>
using namespace std;
list<int> edge[301];int n, m, dp[301][301];
void dfs(int x)
{
    for(auto &v : edge[x])
    {
        dfs(v);
        for(int i = m + 1;i >= 1;--i)
            for(int j = 1;j < i;++j)
                dp[x][i] = max(dp[x][i], dp[v][j] + dp[x][i - j]);
    }
}
int main()
{
    cin >> n >> m;
    for(int i = 1, v;i <= n;++i)
        cin >> v >> dp[i][1], edge[v].push_back(i);
    dfs(0);
    cout << dp[0][m + 1];
    return 0;
}

T2

分层图模板题。

建图

本题唯一难点。

想象一下原图分成上下分离的,平行的若干层。

平行的层中对应点有 0 权边。

每条边在相邻层的对应点上也有 0 权边。

需要算从第一层的 s 到最后一层的 t

相信大家不难看出,0 权边模拟的是免费机票。

每用一次免费机票,就往下一层。

如果不用就直接到下一层的对应点,也存在 0 权边。

那总共最多用 k 次,总共 k+1 层。

那这题就没了。

代码

可以用 x+yn 表示 x 点在第 y 层上的编号,最上面是 0 层。

#include <iostream>
#include <list>
#include <queue>
#include <functional>
#include <utility>
#include <cstring>
using namespace std;
list<pair<int, int> > edge[2000001];int n, m, k, s, t, u, v, w, dis[2000001], vis[2000001];
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > q;
void add(int u, int v, int w) {edge[u].push_back(make_pair(w, v));}
int main()
{
    cin >> n >> m >> k >> s >> t;
    for(int i = 0;i < k;++i)
        for(int j = 0;j < n;++j)
            add(j + i * n, j + (i + 1) * n, 0);
    for(int i = 0;i < m;++i)
    {
        cin >> u >> v >> w;
        for(int j = 0;j < k;++j)
        {
            add(u + j * n, v + j * n, w);
            add(v + j * n, u + j * n, w);
            add(u + j * n, v + (j + 1) * n, 0);
            add(v + j * n, u + (j + 1) * n, 0);
        }
        add(u + k * n, v + k * n, w);
        add(v + k * n, u + k * n, w);
    }
    memset(dis, 0x3f, sizeof dis);
    q.push(make_pair(0, s));dis[s] = 0;
    while(!q.empty())
    {
        int u = q.top().second;q.pop();
        if(vis[u]) continue;
        vis[u] = 1;
        for(auto &i : edge[u])
            if(dis[i.second] > dis[u] + i.first)
            {
                dis[i.second] = dis[u] + i.first;
                q.push(make_pair(dis[i.second], i.second));
            }
    }
    cout << dis[t + k * n];
    return 0;
}

T3

暴搜大家应该都会写。

这里就是比一般的遍历图多了个数组,记录游览过的文明。

游览一个点前要判断:

  1. 这个点没到过
  2. 这个点的文明没访问过
  3. 这个点的文明和之前访问的不冲突

然后就是 dfs 板子了。

这是学长的代码(自己加的注释):

#include<bits/stdc++.h>
#define R register
#define ll long long 
#define N 4010
#define p 20000528
#define inf 0x3f3f3f3f
#pragma GCC optimize(3) //手动开O3最好不要学
using namespace std;
inline int read() 
{
    R int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return x*f;
}
void out(R int x)
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)out(x/10);putchar(x%10+'0');
}
int n,k,m,s,t;
int c[N],a[N][N],head[N],net[N],to[N],tot,w[N];
void add(R int x,R int y,R int z)
{
    to[++tot]=y;
    w[tot]=z;
    net[tot]=head[x];
    head[x]=tot;
}
int ans=inf;
int vc[N],v[N],cnt;
void dfs(R int x,R int y,R int stp)
{
    if(stp>ans)return; //剪枝,如果现在搜到的比当前答案大,就没必要搜了
    if(x==y)ans=min(stp,ans); //到了
    for(R int i=head[x];i;i=net[i])
    {
        int vj=to[i];
        int f=0;
        for(R int j=1;j<=k;j++)
        if(vc[j]&&a[j][c[vj]]) //如果之前访问过的文明和下一个冲突
        {
            f=1;break;
        }
        if(!v[vj]&&!vc[c[vj]]&&!f) //三个条件
        {
            v[vj]=1,vc[c[vj]]=1;
            dfs(vj,y,stp+w[i]);
            v[vj]=0,vc[c[vj]]=0;
        }
    }
}
int main()
{
    n=read(),k=read(),m=read(),s=read(),t=read();
    for(R int i=1;i<=n;i++)c[i]=read();
    for(R int i=1;i<=k;i++)
        for(R int j=1;j<=k;j++)
            a[i][j]=read();
    for(R int i=1;i<=m;i++) {
        R int x,y,z;
        x=read(),y=read(),z=read();
        add(x,y,z);
        add(y,x,z);
    }
    v[t]=1,vc[c[t]]=1;
    dfs(t,s,0);
    out(ans==inf?-1:ans);
    return 0;
}   

T4

没啥好说的,纯模拟题。

这里只需要注意越界问题就行了(即[减后+n防负数]和[%n防溢出])

#include <iostream>
#include <string>
using namespace std;
struct p
{
    bool f;string s;
}a[100001];
int n, m, ans, x, y;
int main()
{
    cin >> n >> m;
    for(int i = 0;i < n;++i)
        cin >> a[i].f >> a[i].s;
    for(int i = 0;i < m;++i)
    {
        cin >> x >> y;
        if(!x && !a[ans].f) ans = (ans + n - y) % n;
        else if(!x && a[ans].f) ans = (ans + y) % n;
        else if(x && !a[ans].f) ans = (ans + y) % n;
        else if(x && a[ans].f) ans = (ans + n - y) % n;
    }
    cout << a[ans].s;
    return 0;
}
posted @   Jijidawang  阅读(2)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示