Codeforces Round #260 (Div. 1) A.Boredom

本文介绍了一种动态规划(DP)算法的应用案例,针对一个整数序列,通过合理的元素删除策略来最大化获得的总价值。文章详细解析了算法思路,包括状态定义、状态转移方程及AC代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天是下定决心好好练DP的第一天,从1600的DP开始做起。

题目链接:点击打开原题目。

题意:给一个长度位n的整数序列,每次选择一个数字删除,删除这个数的同时比这个数大1和小1的数全部被删掉,每次得到的值就是你当前选择的这个数的价值,问能得到的值的总和最大是多少。

思路:在这个序列里面选数的限制条件就是当你选了a[i],你就不能选择a[i]+1和a[i]-1了,因为已经在你选择a[i]的时候被删除了。同时相同价值的数所面临的情况是相同的,能选就都可以选,不能选就都在之前的某一轮被删除了,所以对于所有的数来说,选择他们的关键就是他们的大小,再深入一点想,一个数a[i]的状态只和a[i]-1和a[i]+1有关,将所有的数按照价值和出现的个数存起来,从小到大排序,如果对第i个数a[i]来说存在a[i]-1,,那这个数一定是第i个数,如果存在a[i]+1,一定是第i+1个数,所以枚举下标就好了。

①. dp[i][0]表示第i个数不选

当前第i个不选,那么i-1选不选都对它没影响,所以取前面最大的。得到状态转移方程:dp[i][0]=max(dp[i-1][1],dp[i-1][0]);

②. dp[i][1]表示第i个数要选

那么第i-1个数如果大小是a[i]-1的话,他就不能选。此时状态转移方程是:dp[i][1]=dp[i-1][0]+a[i].va*a[i].cnt,(a[i].va表示这个数的值,a[i].cnt表示这个数的个数,当前这个数都选了,肯定要加上它的价值)

当第i-1个数不是a[i]-1,那么两个数的选择是相互独立的,此时只需要把当前这个数能贡献的答案加上前面最大的。

dp[i][1]=max(dp[i-1][0],dp[i-1][1])+a[i].va*a[i].cnt;

到这里这道题就算是做完了(要开long long)。

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node
{
    int va;
    int cn;
    bool operator<(const node&aa)const
    {
        return aa.va>va;
    }
} a[100010];
ll dp[100010][3];
int cnt[100010];
int main()
{
    int n,num;
    scanf("%d",&n);
    int maxx=-1;
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&num);
        cnt[num]++;
        maxx=max(maxx,num);
    }
    int nn=0;
    for(int i=1; i<=maxx; i++)
    {
        if(cnt[i]!=0)
            a[++nn].va=i,a[nn].cn=cnt[i];
    }
    sort(a+1,a+nn+1);
    dp[1][1]=(ll)a[1].va*a[1].cn;
    dp[1][0]=0;
    for(int i=2;i<=nn;i++)
    {
        if(a[i].va-a[i-1].va==1)
        {
            dp[i][0]=max(dp[i-1][1],dp[i-1][0]);
            dp[i][1]=dp[i-1][0]+(ll)a[i].va*a[i].cn;
        }
        else
        {
            dp[i][0]=max(dp[i-1][1],dp[i-1][0]);
            dp[i][1]=max(dp[i-1][0],dp[i-1][1])+(ll)a[i].va*a[i].cn;
        }
    }
    ll ans=max(dp[nn][1],dp[nn][0]);
    printf("%lld\n",ans);
    return 0;
}


 

### Codeforces Round 260 Div. 1 题目及题解 #### A. Vasya and Multisets 在这道题目中,Vasya有一个由n个整数组成的序列。目标是通过将这些数分成若干组,使得每组中的所有数都相同,并且尽可能减少分组的数量。 为了实现这一目的,可以利用贪心算法来解决这个问题。具体来说,在遍历输入数据的同时维护当前最大频率计数器,对于每一个新遇到的不同数值增加一个新的集合[^1]。 ```cpp #include <bits/stdc++..h> using namespace std; void solve() { int n; cin >> n; unordered_map<int, int> freq; for (int i = 0; i < n; ++i) { int x; cin >> x; freq[x]++; } int maxFreq = 0; for (auto& p : freq) { maxFreq = max(maxFreq, p.second); } cout << maxFreq << "\n"; } ``` #### B. Pashmak and Graph 此问题涉及图论领域的一个经典最短路径计算案例。给定一张带权无向图以及起点S和终点T,要求求出从S到T经过至少一条边后的最小花费总和。 Dijkstra算法适用于此类场景下的单源最短路径查询任务。初始化距离表dist[]为无穷大(INF),仅设置起始节点的距离为零;随后借助优先队列选取未访问过的最近邻接顶点u更新其相邻结点v至目前为止所知的最佳到达成本min{dist[u]+w(u,v)}直至找到终止条件即抵达目的地t或处理完毕所有可达区域内的候选者为止。 ```cpp typedef pair<long long,int> pli; const long long INF = LLONG_MAX / 3; struct Edge { int to, cost; }; vector<Edge> G[MAX_V]; long long d[MAX_V]; bool dijkstra(int s, int t){ priority_queue<pli,vector<pl>,greater<pl>> que; fill(d,d+MAX_V,INF); d[s]=0; que.push(pli(0,s)); while(!que.empty()){ pli p=que.top();que.pop(); int v=p.second; if(d[v]<p.first) continue; for(auto e:G[v]){ if(d[e.to]>d[v]+e.cost){ d[e.to]=d[v]+e.cost; que.push(pli(d[e.to],e.to)); } } } return d[t]!=INF; } ``` #### C. DZY Loves Colors 这是一道关于颜色染色的问题。给出长度为N的一维网格,初始状态下每个格子都有一个默认的颜色编号。现在有M次操作机会改变某些位置上的色彩值,最终目的是统计整个条带上共有几种不同的色调存在。 采用离散化技术预处理原始输入并记录下各段连续同色区间的端点坐标范围,之后针对每一次修改请求动态调整受影响部分的信息结构体(如线段树),最后依据累积的结果得出答案。 ```cpp // 假设已经实现了上述提到的数据结构 SegmentTree 和 update 函数 SegmentTree st; for(int i=1;i<=m;++i){ int l,r,c; scanf("%d%d%d",&l,&r,&c); update(l,r,c); } printf("%lld\n",st.query()); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值