D. Kousuke's Assignment
题目链接👈
题目描述🥰

题目思路😀
首先我们可以理解题目的意思,通过对样例的解析,我们可以发现题目需要求的是最多的非重叠的美丽区间的数量(题目里面的最大看错了还误导了好久🤣)
题目本质上就是求出最大的非交叉线段数量,所有线段的和都等于零。
而实际上这个问题也就是一个非常经典的动态规划问题,我们可以用dp[i]来表示前i个元素里面的最多的非重叠区间个数,同时我们可以用一个前缀数组pre[i]来计算前i个元素的总和,当pre[i]==pre[j] ( i < j )时,也就说明在第i个元素和第j个元素之间的线段的和为0,所以我们dp[j]=max(dp[i]+1,dp[j])
仅需选择离j最大的那一个i,但是为什么呢?
因为如果我们选择k<i的线段,那么我们就会错过线段[k+1,i]。由于这个遗漏,我们将无法找到dp[j]的正确答案。
AC代码🧠
// Problem: D. Kousuke's Assignment
// Contest: Codeforces - Codeforces Round 981 (Div. 3)
// URL: https://codeforces.com/contest/2033/problem/D
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define dev1(a) cout << #a << '=' << a << endl;
#define dev2(a, b) cout << #a << " = " << a << " " << #b << " = " << b << endl;
#define dev3(a, b, c) cout << #a << " = " << a << " " << #b << " = " << b << " " << #c << " = " << c << endl;
#define dev4(a, b, c, d) cout << #a << " = " << a << " " << #b << " = " << b << " " << #c << " = " << c << " " << #d << " = " << d << endl;
#define dev5(a, b, c, d, e) cout << #a << " = " << a << " " << #b << " = " << b << " " << #c << " = " << c << " " << #d << " = " << d << " " << #e << " = " << e << endl;
#define vec(a) \
for (int i = 0; i < a.size(); i++) \
cout << a[i] << ' '; \
cout << endl;
#define darr(a, _i, _n) \
cout << #a << ':'; \
for (int ij = _i; ij <= _n; ij++) \
cout << a[ij] << ' '; \
cout << endl;
#define cin(a,n) \
for(int i=0;i<n;i++) \
cin>>a[i];
#define endl "\n"
#define pow pim
int pim(int a,int k)
{
int res=1;
if(a==0)return 0;
while(k)
{
if(k&1)res=(int)res*a;
k>>=1;
a=(int)a*a;
}
return res;
}
#define fi first
#define se second
#define caseT \
int T; \
cin >> T; \
while (T--)
#define int long long
// #define int __int128
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int MOD = 99999999;
// const int N = ;
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
int lcm(int a, int b)
{
return a * b / gcd(a, b);
}
inline int read()
{
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
void solve()
{
int n;
cin>>n;
vector<int>a(n+1);
for(int i=1;i<=n;i++)cin>>a[i];
map<int,int>hash;
//记录的是当前前缀和的最近的下标
vector<int>pre(n+1,0);
//记录的是前缀和
vector<int>first(n+1);
//first[i]记录的是当前i位置相同前缀和的最近的下标
hash[0]=0;
for(int i=1;i<=n;i++)
{
pre[i]=pre[i-1]+a[i];
//因为这里面的hash值记录的是前缀的最近的下标,所以不能用hash[pre[i]]来判断
if(hash.find(pre[i])==hash.end())
{
//如果这个前缀是第一次出现的话,那么就把这个前缀位置赋值为-1
first[i]=-1;
}else first[i]=hash[pre[i]];
hash[pre[i]]=i;
}
vector<int>dp(n+1,0);
//dp[i]表示的是前i个元素的最大非重叠美丽区间的数量
for(int i=1;i<=n;i++)
{
dp[i]=max(dp[i],dp[i-1]);
if(first[i]!=-1)dp[i]=max(dp[i],dp[first[i]]+1);
}
cout<<*max_element(dp.begin()+1,dp.begin()+n+1)<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
caseT
solve();
return 0;
}
/*
*/
posted on
浙公网安备 33010602011771号