cf自训6
作者:互联网
cf946D 背包+区间dp 好题
/* 先预处理出每行消去i个1后可以的到的最小时间: 先求每行的前缀和,枚举左端点和右端点,消去的1 cost=tot-sum[r]+sum[l-1],区间长度=r-l+1 time[cost]=min(time[cost],len) 然后再进行行间处理: 就是n行删k个,转化为n组物品,然后总代价是k 分组背包做一下完事 dp[i][j]表示到第i行删了j个的最少上课时间 */ #include<bits/stdc++.h> using namespace std; #define maxn 505 int n,m,K,mp[maxn][maxn]; int t[maxn][maxn],sum[maxn][maxn],dp[maxn][maxn]; void init(){ memset(t,0x3f,sizeof t); for(int i=1;i<=n;i++){ t[i][sum[i][m]]=0;//全删完的情况 for(int l=1;l<=m;l++) for(int r=l;r<=m;r++){ int len=r-l+1; int cost=sum[i][m]-(sum[i][r]-sum[i][l-1]); t[i][cost]=min(t[i][cost],r-l+1); } } /*for(int i=1;i<=n;i++){ for(int j=0;j<=sum[i][m];j++) cout<<t[i][j]<<" "; puts(""); }*/ } int main(){ cin>>n>>m>>K; char ch; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ cin>>ch; mp[i][j]=ch-'0'; sum[i][j]=sum[i][j-1]+mp[i][j]; } init(); memset(dp,0x3f,sizeof dp); for(int j=0;j<=K;j++)dp[1][j]=t[1][j]; for(int i=2;i<=n;i++) for(int j=0;j<=K;j++) for(int c=0;c<=j;c++) dp[i][j]=min(dp[i][j],dp[i-1][j-c]+t[i][c]); int ans=0x3f3f3f3f; for(int j=0;j<=K;j++) ans=min(ans,dp[n][j]); cout<<ans<<endl; }View Code
cf893d 线段树区间更新,可以傻逼贪心做
/* 在每个0之前都要变成非负,这个非负数要尽量大。。 先求前缀和数组,从左往右扫,找到第一个在0前的负值点,将其提高到允许的最大值x,然后后面统一+x 线段树维护区间最大前缀和,然后从左往右扫,扫到ai=0时进行询问 如果该点前缀和小于0那么将其提高,提高的上限lim=d-后面区间最大前缀和,如果lim+当前值<0,那么不可行 反之不进行操作 对前缀和先判一下。。 */ #include<bits/stdc++.h> using namespace std; #define maxn 100005 #define ll long long ll ans,n,d,a[maxn],sum[maxn],flag; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 ll Max[maxn<<2],lazy[maxn<<2]; void pushup(int rt){Max[rt]=max(Max[rt<<1],Max[rt<<1|1]);} void pushdown(int rt){ if(lazy[rt]){ lazy[rt<<1]+=lazy[rt];lazy[rt<<1|1]+=lazy[rt]; Max[rt<<1]+=lazy[rt];Max[rt<<1|1]+=lazy[rt]; lazy[rt]=0; } } void build(int l,int r,int rt){ if(l==r){Max[rt]=sum[l];return;} int m=l+r>>1; build(lson);build(rson); pushup(rt); } void update(int L,int R,ll val,int l,int r,int rt){ if(L<=l &&R>=r){lazy[rt]+=val;Max[rt]+=val;return;} int m=l+r>>1; pushdown(rt); if(L<=m)update(L,R,val,lson); if(R>m)update(L,R,val,rson); pushup(rt); } ll query(int L,int R,int l,int r,int rt){ if(L<=l && R>=r)return Max[rt]; pushdown(rt); int m=l+r>>1;ll res=-0x3f3f3f3f; if(L<=m)res=query(L,R,lson); if(R>m)res=max(res,query(L,R,rson)); return res; } int main(){ cin>>n>>d; for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=n;i++){sum[i]=sum[i-1]+a[i];if(sum[i]>d)flag=1;} if(flag){ puts("-1"); return 0; } build(1,n,1); for(int i=1;i<=n;i++){ if(a[i]==0){ int tmp=query(i,i,1,n,1); if(tmp>=0)continue; else{ int Max=query(i,n,1,n,1); int lim=d-Max; if(tmp+lim<0 || flag){ puts("-1"); return 0; } else { update(i,n,lim,1,n,1); ans++; } } } } cout<<ans<<endl; }View Code
cf797E 复杂度均摊成根号n级别,新题
#include<bits/stdc++.h> using namespace std; #define maxn 120005 int q,n,a[maxn]; int dp[maxn][340];//dp[i][j]表示在位置i(p=i),k=j时的跳跃步数,dp[i][j]=dp[i+a[i]+j][j]+1 void init(){ for(int j=1;j<=330;j++) for(int i=n;i>=1;i--){ if(i+a[i]+j>n)dp[i][j]=1; else dp[i][j]=dp[i+a[i]+j][j]+1; } } int main(){ cin>>n; for(int i=1;i<=n;i++)cin>>a[i]; init(); cin>>q; while(q--){ int ans=0,p,k; cin>>p>>k; if(k<=330){ cout<<dp[p][k]<<'\n'; continue; } while(p<=n){ p+=a[p]+k; ans++; } cout<<ans<<'\n'; } }View Code
cf46D 线段树区间合并沙比提。。一个vector下标搞错弄了好久
#include<bits/stdc++.h> using namespace std; #define maxn 120005 int q,n,a[maxn]; int dp[maxn][340];//dp[i][j]表示在位置i(p=i),k=j时的跳跃步数,dp[i][j]=dp[i+a[i]+j][j]+1 void init(){ for(int j=1;j<=330;j++) for(int i=n;i>=1;i--){ if(i+a[i]+j>n)dp[i][j]=1; else dp[i][j]=dp[i+a[i]+j][j]+1; } } int main(){ cin>>n; for(int i=1;i<=n;i++)cin>>a[i]; init(); cin>>q; while(q--){ int ans=0,p,k; cin>>p>>k; if(k<=330){ cout<<dp[p][k]<<'\n'; continue; } while(p<=n){ p+=a[p]+k; ans++; } cout<<ans<<'\n'; } }View Code
标签:rt,int,cf,cin,自训,init,maxn,dp 来源: https://www.cnblogs.com/zsben991126/p/10648277.html