读题读了很长时间,本质其实就是问排序后序列中有没有断档,可以桶排也可以Sort然后一个个判断。
#include
using namespace std;
int cas,n,a[55];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>cas;
while(cas--)
{
memset(a,0,sizeof(a));
cin>>n;
for(int i=1; i<=n; ++i)
cin>>a[i];
sort(a+1,a+n+1);
int flag=1;
for(int i=2; i<=n; ++i)
if(abs(a[i]-a[i-1])>1)
{
cout<<"NO"<<endl;
flag=0;
break;
}
if(flag)
cout<<"YES"<<endl;
}
}
对于candy和orange分别在输入的时候找出最小值作为标准,再跑一遍顺序,做min(ai,bi)
次同减,再对剩下的一方操作max(ai,bi)-min(ai,bi)
次,两者相加,相当于每一对的最大操作次数就是max(ai,bi)
。
#include
#define int long long
using namespace std;
const int inf=0x3f3f3f3f;
int cas,n,a[55],b[55],mina,minb,ans;
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>cas;
while(cas--)
{
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
ans=0;
mina=minb=inf;
cin>>n;
for(int i=1; i<=n; ++i)
{
cin>>a[i];
mina=min(mina,a[i]);
}
for(int i=1; i<=n; ++i)
{
cin>>b[i];
minb=min(minb,b[i]);
}
for(int i=1; i<=n; ++i)
{
a[i]-=mina;
b[i]-=minb;
ans+=max(a[i],b[i]);
}
cout<<ans<<endl;
}
}
数据很小,桶装数据,枚举s,再枚举其中一个人的重量i,然后在桶里找有没有重量为s-i的人,如果有就是一对。两人重量相同要特判。
#include
#define int long long
using namespace std;
const int inf=0x3f3f3f3f;
int cas,n,x,w[200],maxw,ans;
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>cas;
while(cas--)
{
memset(w,0,sizeof(w));
ans=maxw=0;
cin>>n;
for(int i=1; i<=n; ++i)
{
cin>>x;
++w[x];
maxw=max(maxw,x);
}
for(int s=2;s<=maxw*2;++s)
{
int tmp=0;
for(int i=1;i<=s/2;++i)
if(i==(s-i))
tmp+=w[i]/2;
else
tmp+=min(w[i],w[s-i]);
ans=max(ans,tmp);
}
cout<<ans<<endl;
}
}
开两个vector,把head为0的和head为1的垃圾分类,遇到0就找head为1的接在上面再整体扔到另一边去,遇到1就找head为0的接头。如果不需要输出第二行,只需要两个变量记录两种head的数量就能做出来。
#include
#define int long long
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=2e5+7;
int cas,n,head0,head1,ans,cnt,mem[maxn];
string s;
queue<int> q0,q1;
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>cas;
while(cas--)
{
cin>>n>>s;
while(!q0.empty()) q0.pop();
while(!q1.empty()) q1.pop();
cnt=head0=head1=0;
for(int i=0;i<n;++i)
{
int num;
if(s[i]=='0')
{
if(head1)
{
num=q1.front();
q0.push(num);
q1.pop();
--head1;
}
else
{
num=++cnt;
q0.push(cnt);
}
++head0;
mem[i]=num;
}
else
{
if(head0)
{
num=q0.front();
q1.push(num);
q0.pop();
--head0;
}
else
{
num=++cnt;
q1.push(cnt);
}
++head1;
mem[i]=num;
}
}
cout<<head0+head1<<endl;
for(int i=0;i<n;++i)
cout<<mem[i]<<" ";
cout<<endl;
}
}
贪心,题目给出n个点n-1条边的树,求出要使得每个叶子节点到根的路径权值和之和sum不大于S的最小操作次数,每次操作可以把某条边的权值/2。当对一条权值为w的边操作后,每一条包含这条边的路径都会受到影响,计该边被使用的次数为t,边对sum的贡献即为t*w
,权值从w变成floor(w/2)
,减少了w-floor(w/2)
,sum就减少了t*(w-floor(w/2))
。因为是通过最少的操作次数使得sum减少到规定的值S,可以想到以操作该边时sum的减少量为排序依据,贪心地选取使sum减少量最大的操作,直至sum<=S。因为每次操作后还要把操作后的边放回去参与下一轮贪心,这里为了方便可以选择优先队列,自定义排序函数。
#pragma GCC optimize(3)
#include
#define int long long
using namespace std;
typedef pair<int,int> P;
struct edge
{
int to,we;
};
struct cmp {
bool operator()(P a, P b)
{
return (a.first*a.second-(a.first/2)*a.second)<(b.first*b.second-(b.first/2)*b.second);
}
};
const int maxn=1e5+7;
int cas,n,s,u,v,w,sum,ans;
vector<edge> g[maxn];
priority_queue<P,vector<P>,cmp> pq;
int dfs(int x,int pre)
{
if(g[x].size()==1&&x!=1)
return 1;
int res=0;
for(auto e:g[x])
if(e.to!=pre)
{
int tmp=dfs(e.to,x);
res+=tmp;
sum+=tmp*e.we;
pq.push(P(e.we,tmp));
}
return res;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>cas;
for(int k=1;k<=cas;++k)
{
while(!pq.empty()) pq.pop();
sum=ans=0;
cin>>n>>s;
for(int i=1;i<=n;++i) g[i].clear();
for(int i=1; i<n; ++i)
{
cin>>u>>v>>w;
g[u].push_back((edge){v,w});
g[v].push_back((edge){u,w});
}
dfs(1,0);
while(sum>s)
{
P p=pq.top();pq.pop();
sum-=(p.first*p.second-(p.first/2)*p.second);
++ans;
pq.push(P(p.first/2,p.second));
}
cout<<ans<<endl;
}
}
贪心+二分,和上一题题面基本一致,给每个边加了一个1或2的价格,求的值变成了最小费用。可能一开始能想到通过性价比贪心,但是其实是错误的,因为只要把sum减到不大于S就行,如果目前只需要再减1就满足目标,面前有一个费用2减100和费用1减1的选项,贪心会选择费用2,从而多浪费了1点费用。在正确的解法中,可以把cost为1和cost为2的边分别贪心,在选择时对于cost相同的选项,当然优先选择减少sum多的。贪心方法依然和E1一样,把贪心结果按照顺序存储起来,并且做前缀和。此时枚举选择cost为1的结果的前i个和cost为2的结果的前j个,如果sum-res[1][i]-res[2][j]<=s
即为可行答案,与ans比较取最小。这里还有明显的二分优化,当枚举i之后,可以从上面的式子得到res[2][j]>=sum-res[1][i]-s
其中右式全部已知,可以以此在res[2]里二分搜索合法的j使得式子成立。
#pragma GCC optimize(3)
#include
#define int long long
using namespace std;
typedef pair<int,int> P;
struct cmp {
bool operator()(P a, P b)
{
return (a.first*a.second-(a.first/2)*a.second)<(b.first*b.second-(b.first/2)*b.second);
}
};
struct edge
{
int to,we,co;
};
const int maxn=1e5+7;
const int inf=0x3f3f3f3f;
int cas,n,s,u,v,w,c,sum,ans;
vector<edge> g[maxn];
vector<int> res[3];
priority_queue<P,vector<P>,cmp> q[3];
int dfs(int x,int pre)
{
if(g[x].size()==1&&x!=1)
return 1;
int res=0;
for(auto e:g[x])
if(e.to!=pre)
{
int tmp=dfs(e.to,x);
res+=tmp;
sum+=e.we*tmp;
q[e.co].push(P(e.we,tmp));
}
return res;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>cas;
while(cas--)
{
cin>>n>>s;
sum=0;
ans=inf;
for(int i=1;i<=n;++i) g[i].clear();
for(int i=1;i<n;++i)
{
cin>>u>>v>>w>>c;
g[u].push_back((edge){v,w,c});
g[v].push_back((edge){u,w,c});
}
while(!q[1].empty()) q[1].pop();
while(!q[2].empty()) q[2].pop();
dfs(1,0);
for(int k=1;k<=2;++k)
{
int tmp=0;
res[k].clear();
res[k].push_back(0);
while(!q[k].empty()&&(sum-tmp>s))
{
P p=q[k].top();q[k].pop();
int val=p.first*p.second-(p.first/2)*p.second;
if(val==0) break;
tmp+=val;
res[k].push_back(val+res[k][res[k].size()-1]);
q[k].push(P(p.first/2,p.second));
}
}
for(int i=0;i<res[1].size();++i)
{
vector<int>::iterator lb=lower_bound(res[2].begin(),res[2].end(),sum-s-res[1][i]);
if(lb==res[2].end())
continue;
else
ans=min(ans,i+2*(lb-res[2].begin()));
}
cout<<ans<<endl;
}
}
离散化+区间DP,因为之前没有做过区间DP的题,代码参考了网页链接。使用的递归的方法DP,f[i][j]表示从i到j这一段内合法的最大线段数,递推式:f[i][j]=(存在线段正好l=i且r=j)+max(f[i+1][j],f[i][k]+f[k+1][j])
,其中i<=k<=j
。
PS:多组数据时尽量用循环,按需初始化,这道题如果每次都把f数组memset,会T。
#include "bits/stdc++.h"
using namespace std;
const int maxn=6010;
int cas,n,cnt,l[maxn],r[maxn],p[maxn],f[maxn][maxn];
vector<int> v[maxn];
int dp(int l,int r)
{
if(f[l][r]!=-1) return f[l][r];
if(l>r) return 0;
f[l][r]=dp(l+1,r);
for(auto subr:v[l])
{
if(subr>=r) continue;
f[l][r]=max(f[l][r],dp(l,subr)+dp(subr+1,r));
}
return f[l][r]=f[l][r]+count(v[l].begin(),v[l].end(),r);
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>cas;
while(cas--)
{
cin>>n;
cnt=0;
for(int i=1;i<=n;++i)
{
cin>>l[i]>>r[i];
p[++cnt]=l[i];
p[++cnt]=r[i];
}
sort(p+1,p+cnt+1);
cnt=unique(p+1,p+cnt+1)-(p+1);
for(int i=1;i<=cnt;++i)
{
v[i].clear();
for(int j=1;j<=cnt;++j)
f[i][j]=-1;
}
for(int i=1;i<=n;++i)
{
l[i]=lower_bound(p+1,p+cnt+1,l[i])-p;
r[i]=lower_bound(p+1,p+cnt+1,r[i])-p;
v[l[i]].push_back(r[i]);
}
cout<<dp(1,cnt)<<endl;
}
}
这样就把坑填完了!