期末预测之最佳阈值
思路:这题中,如果对每一个阈值直接统计预测正确的次数,预测值有0有1,且预测正确的标准不同,耦合在一起统计肯定是减小不了复杂度的。因此分别统计每一个阈值正确预测为1与正确预测为0的次数;当统计每一个阈值正确预测为1的次数时,可以发现计算一个更小的阈值的预测次数可以通过已知更大的阈值的预测次数;同理,在统计每一个阈值正确预测为0的次数时,大阈值的预测数可以通过小阈值的预测数计算。因此可以通过动态规划计算,将输入数据按照阈值大小升序排序后,统计1时从后往前计算,统计0时从前往后计算。还有要注意的是统计0时,因为预测的公式中是<,并没有取等,如果有多个相同的阈值连在一起,需要一个累计变量统计这些相同的阈值预测为0的正确数,在阈值发生改变后,将累加数加到下一个阈值的预测为0的正确数中。最后,将预测0正确数和预测1正确数相加后排个序取最前面的阈值
#include<bits/stdc++.h>
using namespace std;
#define maxn 500005
int m;
struct p{
int y;
int r;
int n;
};
bool cmp(p p1, p p2){
return p1.y<p2.y;
}
bool cmp1(p p1,p p2){
if(p1.n!=p2.n) return p1.n>p2.n;
else return p1.y>p2.y;
}
vector <int> dp0(500005,0);
vector <int> dp1(500005,0);
p pp[maxn];
int main(){
cin>>m;
for(int i=0;i<m;i++){
cin>>pp[i].y>>pp[i].r;
}
sort(pp,pp+m,cmp);
dp0[0]=0;
if(pp[m-1].r==1) dp1[m-1]=1;
else dp1[m-1]=0;
int k=0;
if(pp[0].r==0) k++;
for(int i=1;i<m;i++){
if(pp[i].y>pp[i-1].y){
dp0[i]=dp0[i-1];
dp0[i]+=k;
k=0;
if(pp[i].r==0) k++;
}
else if(pp[i].y==pp[i-1].y){
dp0[i]=dp0[i-1];
if(pp[i].r==0) k++;
}
}
for(int i=m-2;i>=0;i--){
dp1[i]=dp1[i+1];
if(pp[i].r==1) dp1[i]++;
}
for(int i=0;i<m;i++){
pp[i].n=dp0[i]+dp1[i];
}
sort(pp,pp+m,cmp1);
cout<<pp[0].y;
}
邻域均值
思路:其实也是动态规划的思想,利用上一次计算的结果;每一行中,下一个元素的邻域就是上一个元素的邻域向右平移1个单位,二者之间存在大面积的重叠,因此在计算每一行的元素的邻域和与邻域中元素个数时,可以用到同一行中上一个元素的领域和与邻域中元素个数
#include <bits/stdc++.h>
using namespace std;
int n,L,r,t;
vector<vector<int> > matrix(601,vector<int>(601,0));
int ans=0;
double avg;
int main(){
cin>>n>>L>>r>>t;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cin>>matrix[i][j];
}
}
for(int i=0;i<n;i++){
// 确定邻域边界
int x_end=min(0+r,n-1);
int y_begin=max(0,i-r);
int y_end=min(n-1,i+r);
int sum=0;
int num=0;
for(int k=y_begin;k<=y_end;k++){
for(int j=0;j<=x_end;j++){
sum+=matrix[k][j];
num++;
}
}
avg = (double)sum / num;
if(avg<=t) ans++;
for(int j=1;j<=n-1;j++){
// 利用上一次计算的结果
if(j-r<=0 && j+r<=n-1){
for(int k=y_begin;k<=y_end;k++){
sum+=matrix[k][j+r];
num++;
}
}else if(j-r>0 && j+r<=n-1){
for(int k=y_begin;k<=y_end;k++){
sum-=matrix[k][j-r-1];
}
for(int k=y_begin;k<=y_end;k++){
sum+=matrix[k][j+r];
}
}else if(j-r>0 && j+r>n-1){
for(int k=y_begin;k<=y_end;k++){
sum-=matrix[k][j-r-1];
num--;
}
}
avg = (double)sum / num;
if(avg<=t) ans++;
}
}
cout<<ans;
}
非零段划分
思路:也是用到了动态规划,可以观察到,P=i+1时被置为0的元素包括了在P=i时被置为0的元素;因此在计算P=i+1时的非零段个数可以利用到P=i时保存的当前序列的状态与非零段个数;另外需要保存每一个i值在序列中对应的所有的下标,在遍历到i值时将序列中所有的i值置0
#include <bits/stdc++.h>
using namespace std;
vector <int > a(500005);
vector <vector<int> > index_(500005);
vector <int > dp(500005,0);
int max_=0;
int temp;
int main(){
int len=0;
cin>>len;
for(int i=1;i<=len;i++){
cin>>a[i];
max_=max(max_,a[i]);
index_[a[i]].push_back(i);
}
a[0]=0;
a[len+1]=0;
dp[0]=0;
for(int i=0;i<=len;i++){
if(a[i]!=0&&a[i+1]==0) dp[0]++;
}
int ans=dp[0];
for(int i=1;i<=max_;i++){
dp[i]=dp[i-1];
if(index_[i].size()==0) continue;
for(int j=0;j<index_[i].size();j++){
a[index_[i][j]]=0;
if(a[index_[i][j]-1]!=0 && a[index_[i][j]+1]!=0){
dp[i]++;
}else if(a[index_[i][j]-1]==0 && a[index_[i][j]+1]==0){
dp[i]--;
}
}
ans=max(ans,dp[i]);
}
cout<<ans;
}
序列查询新解
思路:这一题优化终于不是用动态规划来通过利用之前的计算结果来计算当前结果;如下图,通过观察样例发现,f(i)的值被数组A的元素划分成多个段,每一个段[A[i],A[i+1])内f(i)的值是不变的;并且观察到g(i)从始至终都是均匀变化的,每相隔r个单位变化一次;因此,整个计算的过程就变成了遍历每一个段,再将每个段内再划分成宽度为r的子段,该子段可以通过|f(i)-g(i)|×r直接计算,避免了大量相同的不必要的重复计算。另外,要注意一些边界的处理,这里的处理方法是,当发现下一个宽度为r的子段超过了当前段的范围,则将该子段截断,只计算在当前段范围内的部分,使用j代表在段内遍历的索引,同时j也是子段的起始下标,则计算公式为|f(i)-g(i)|×(A[i+1]-j),同时暂时改变下一个段中的第一个子段的宽度为(r-(A[i+1]-j)),只需要用到一个标志位指示是否存在这种边界问题即可。最后,由于最后一个段是[A[n],N),为了方便遍历段,令A[n+1]=N。
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int a[100000];
int main(){
long long int N;
int n;
cin>>n>>N;
a[0]=0;
for(int i=1;i<=n;i++) cin>>a[i];
int r=N/(n+1);
a[n+1]=N;
int fx,gx=0;
int dr,ddr=0;
int flag=0;
long long int error=0;
for(int i=0;i<=n;i++){
fx=i;
for(int j=a[i];j<a[i+1];j+=dr){
gx=j/r;
if(flag==1){
dr=ddr;
flag=0;
}else{
dr=r;
}
if(j+dr>=a[i+1]){
error+=abs(fx-gx)*(a[i+1]-j);
ddr=dr-(a[i+1]-j);
flag=1;
}else{
error+=abs(fx-gx)*dr;
}
}
}
cout<<error;
}