1 条题解
-
0
模拟赛场切了,但仔细算一下感觉题解区有些人的复杂度是错的,故来写一发题解。
思路其实很简单,显然不能直接暴力枚举横着和竖着放哪些。
考虑枚举横着放哪些,竖着的二分加贪心去放,但是直接算是 级别的,感觉难以通过。
可以预处理出两个竖着的线之间的最大值,降低二分时 check 的复杂度,计算发现是 次方级别,而且数据较水,轻松通过,最慢的点为 ,应该是你谷最快。
#include<bits/stdc++.h> #define ll long long #define R register #define F(i,a,b) for(int i = (a);i<=(b);i++) using namespace std; inline int read(){R int x=0,t=1;R char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') t=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*t;} const int N=22+10; int n,m,r,s,a[N][N],p[N]; ll sm[N][N],sum,b[N][N],mx[N][N]; inline bool check(ll x) { int last=1,r1=1; for(int i = 1;i<=s;i++){ if(last>m) return 1; if(mx[last][last]>x) return 0; while(r1<=m && mx[last][r1]<=x){ r1++; } r1--; if(mx[last][r1]<=x){ r1++; last=r1; } } if(last>m) return 1; if(mx[last][m]<=x){ return 1; } return 0; } inline void work() { p[r+1]=n; for(int i = 1;i<=m;i++){ for(int j = 1;j<=r+1;j++){ b[j][i]=0; for(int k = p[j-1]+1;k<=p[j];k++){ b[j][i]+=a[k][i]; } } } for(int i = 1;i<=r+1;i++){ for(int j = 1;j<=m;j++){ sm[i][j]=sm[i][j-1]+b[i][j]; } } for(int i = 1;i<=m;i++){ for(int j = i;j<=m;j++){ ll maxn=0; for(int k = 1;k<=r+1;k++){ maxn=max(maxn,sm[k][j]-sm[k][i-1]); } mx[i][j]=maxn; } } if(!check(sum)) return; ll l=0,r=sum,ans=1e18; while(l<=r){ int mid=l+r>>1; if(check(mid)){ ans=mid; r=mid-1; } else l=mid+1; } sum=min(sum,ans); return; } void dfs(int x) { if(x>r){ work(); return; } for(int i = p[x-1]+1;i<=n;i++){ p[x]=i; dfs(x+1); p[x]=0; } return; } signed main() { n=read(),m=read(),r=read(),s=read(); F(i,1,n){ F(j,1,m){ a[i][j]=read(); sum+=a[i][j]; } } dfs(1); cout << sum << '\n'; return 0; } /* */
- 1
信息
- ID
- 2822
- 时间
- 1000ms
- 内存
- 32MiB
- 难度
- 9
- 标签
- 递交数
- 11
- 已通过
- 5
- 上传者