「小组联考」第二周一次考试
目录
T1 「SCOI2005」最大子矩阵
题目
考场思路
刚开始看这道题,是看到它的数据范围,觉得又可以骗过一道题。
结果出来后,发现其实这道题是我得分最低的一道题...
考试的时候也想到 \(dp\),结果最后 \(WA\) 了,只拿到 \(30pts\)。
附个考场代码。
#include <cstdio>
#include <cstring>
#include <iostream>
#define inf 0x3f3f3f3f
using namespace std;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,k,ans=-inf,a[105][3],dp[105][11][4],pre[105][3];
int main()
{
n=read();m=read();k=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
a[i][j]=read();
pre[i][j]=pre[i-1][j]+a[i][j];
}
memset(dp,-0x3f,sizeof dp);
dp[0][0][0]=dp[0][0][1]=dp[0][0][2]=dp[0][0][3]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=k;j++)
{
int M1=inf,M2=inf;
for(int l=i-1;l>=0;l--)
{
M1=min(M1,pre[l][1]);
if(m==2) M2=min(M2,pre[l][2]);
dp[i][j][0]=max(dp[i][j][0],dp[l][j-1][3]+pre[i][1]-M1);
if(m==2) dp[i][j][1]=max(dp[i][j][1],dp[l][j-1][3]+pre[i][2]-M2);
dp[i][j][0]=max(dp[i][j][0],max(dp[l][j][2],dp[l][j-1][2])+pre[i][1]-pre[l][1]);
if(m==2) dp[i][j][1]=max(dp[i][j][1],max(dp[l][j][2],dp[l][j-1][2])+pre[i][2]-pre[l][2]);
if(m==2)
{
dp[i][j][2]=max(dp[i][j][2],dp[l][j-2][3]+pre[i][1]-M1+pre[i][2]-M2);
dp[i][j][2]=max(dp[i][j][2],dp[l][j-1][3]+pre[i][1]-pre[l][1]+pre[i][2]-pre[l][2]);
}
}
dp[i][j][3]=max(dp[i][j][0],max(dp[i][j][1],dp[i][j][2]));
}
ans=max(ans,dp[i][k][3]);
}
printf("%d\n",ans);
}
题解
方法一 朴素 \(dp\)
定义状态 \(dp[i][j][k]\):左边一列选到 \(i\) ,右边一列选到 \(j\) 时,一共选了 \(k\) 个矩形时的最大值。
其实,在这个状态定义下,\(m=1\) 与 \(m=2\) 的情况是可以同时解决的,在 \(m=1\) 的时候,只需要将第二列当成全都是 \(0\) 的情况即可。
那么状态转移呢?我们分成四种情况讨论
- 什么都不干,\(dp[i][j][k]=max\{dp[i-1][j][k],d[i][j-1][k]\}\)
- 只选左边的一列,\(dp[i][j][k]=max\{dp[i][j][k],d[l-1][j][k-1]+s[i][1]-s[l-1][1]|l\in [1,i]\}\)
- 只选右边的一列,\(dp[i][j][k]=max\{dp[i][j][k],d[i][l-1][k-1]+s[j][2]-s[l-1][2]\}\)
- 选一个宽度为 \(2\) 的矩阵 \((\) 一定满足 \(i=j\)\()\),\(dp[i][j][k]=max\{dp[i][j][k],dp[l-1][l-1][k-1]s[i][1]+s[i][2]-s[l-1][1]-s[l-1][2]|l\in [1,i]\}\)
只需要再进行一些细节处理即可,时间复杂度 \(O(n^2\cdot k)\)。
#include<bits/stdc++.h>
using namespace std;
template<class T>inline void qread(T& x){
char c;bool f=false;x=0;
while((c=getchar())<'0'||'9'<c)if(c=='-')f=true;
for(x=(c^48);'0'<=(c=getchar())&&c<='9';x=(x<<1)+(x<<3)+(c^48));
if(f)x=-x;
}
template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
inline int rqread(){
char c;bool f=false;int x=0;
while((c=getchar())<'0'||'9'<c)if(c=='-')f=true;
for(x=(c^48);'0'<=(c=getchar())&&c<='9';x=(x<<1)+(x<<3)+(c^48));
return f?-x:x;
}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
const int MAXN=100;
const int MAXM=2;
const int MAXK=10;
int N,M,K,dp[MAXN+5][MAXN+5][MAXK+5];
int a[MAXN+5][MAXM+5],s[MAXN+5][MAXM+5],ans;
inline void init(){
qread(N,M,K);
for(int i=1;i<=N;++i)for(int j=1;j<=M;++j){
qread(a[i][j]);
s[i][j]=s[i-1][j]+a[i][j];
}
}
inline void getDp(){
for(int k=1;k<=K;++k)for(int i=0;i<=N;++i)for(int j=0;j<=N;++j)
{
if(i)dp[i][j][k]=dp[i-1][j][k];
if(j)dp[i][j][k]=max(dp[i][j][k],dp[i][j-1][k]);
for(int l=1;l<=i;++l)dp[i][j][k]=Max(dp[i][j][k],dp[l-1][j][k-1]+s[i][1]-s[l-1][1]);
for(int l=1;l<=j;++l)dp[i][j][k]=Max(dp[i][j][k],dp[i][l-1][k-1]+s[j][2]-s[l-1][2]);
if(i==j)for(int l=1;l<=i;++l)dp[i][j][k]=Max(dp[i][j][k],dp[l-1][l-1][k-1]+s[i][1]+s[i][2]-s[l-1][1]-s[l-1][2]);
if(k==K)ans=Max(ans,dp[i][j][k]);
}
}
signed main(){
init();
getDp();
printf("%d\n",ans);
return 0;
}
方法二 分块思想
膜拜 \(JZM\) 大佬,时间复杂度在 \(O(n\cdot k)\) 左右,少了一个 \(n\) 的复杂度。
具体怎么做,等我有时间再写...
T2 「一本通 6.1 例 1」序列的第 k 个数
题目
考场思路即正解
一道打卡题,或者叫人口普查,不用过多解释...
#include<bits/stdc++.h>
using namespace std;
#define int long long
template<class T>inline void qread(T& x){
char c;bool f=false;x=0;
while((c=getchar())<'0'||'9'<c)if(c=='-')f=true;
for(x=(c^48);'0'<=(c=getchar())&&c<='9';x=(x<<1)+(x<<3)+(c^48));
if(f)x=-x;
}
template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
inline int rqread(){
char c;bool f=false;int x=0;
while((c=getchar())<'0'||'9'<c)if(c=='-')f=true;
for(x=(c^48);'0'<=(c=getchar())&&c<='9';x=(x<<1)+(x<<3)+(c^48));
return f?-x:x;
}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
const int MOD=200907;
inline int qkpow(int a,int n){
int ret=1;
for(;n>0;n>>=1){
if(n&1)(ret*=a)%=MOD;
(a*=a)%=MOD;
}
return ret;
}
int T,a,b,c,k;
signed main(){
qread(T);
while(T--){
qread(a,b,c,k);
if(c*1.0/b==b*1.0/a)printf("%d\n",a%MOD*qkpow(b/a,k-1)%MOD);
else printf("%d\n",(a%MOD+(k-1)%MOD*(b-a)%MOD)%MOD);
}
return 0;
}
T3 「雅礼国庆 2017 Day6」Star Way To Heaven
题目
考场思路
考场时差点想到正解,一道隐形的图论题。
首先考虑二分答案,然后用 \(dijkstra\) 跑一遍,看能否有一条路能够连接上下边界。
如果能,说明我们这个答案过大了,否则,我们还可以再继续调大二分边界。
时间复杂度在 \(O(k^2\cdot log^m_2)\) 左右,但是这个 \(log^m_2\) 却被卡掉了。
结果最后 \(80pts\) ...
一气之下,我把 \(spfa\)、双 \(bfs\) 都打了一遍,结果还是卡在 \(80pts\) 不动...
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
#define int long long
template<class T>inline void qread(T& x){
char c;bool f=false;x=0;
while((c=getchar())<'0'||'9'<c)if(c=='-')f=true;
for(x=(c^48);'0'<=(c=getchar())&&c<='9';x=(x<<1)+(x<<3)+(c^48));
if(f)x=-x;
}
template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
inline int rqread(){
char c;bool f=false;int x=0;
while((c=getchar())<'0'||'9'<c)if(c=='-')f=true;
for(x=(c^48);'0'<=(c=getchar())&&c<='9';x=(x<<1)+(x<<3)+(c^48));
return f?-x:x;
}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
const int MAXK=6000;
struct star{
double x,y;
star(){}
star(const double X,const double Y):x(X),y(Y){}
// bool operator<(const star a)const{return y>a.y;}
}pos[MAXK+5];
/*
struct node{
double x,y;int u;
node(){}
node(const double X,const double Y,const int U):x(X),y(Y),u(U){}
};
*/
struct node{
int u,flg;
node(){}
node(const int U,const int F):u(U),flg(F){}
};
inline double dis(const star a,const star b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double n,m;
int k;
inline bool check(const double d){
queue<node>Q;int vis[MAXK+5];
for(int i=1;i<=k;++i){
vis[i]=0;
if(pos[i].y<=d*2)Q.push(node(i,1)),vis[i]=1;
if(m-pos[i].y<=d*2){
if(vis[i]==1)return false;
Q.push(node(i,-1)),vis[i]=-1;
}
}
while(!Q.empty()){
node now=Q.front();Q.pop();
for(int i=1;i<=k;++i)if(i!=now.u&&vis[i]!=now.flg){
if(dis(pos[i],pos[now.u])>d*2)continue;
if(vis[i]!=0&&vis[i]!=now.flg)return false;
Q.push(node(i,now.flg)),vis[i]=now.flg;
}
}
return true;
}
signed main(){
scanf("%lf %lf",&n,&m);qread(k);
for(int i=1;i<=k;++i)scanf("%lf %lf",&pos[i].x,&pos[i].y);
// sort(pos+1,pos+k+1);
double l=0,r=m/2,ans,mid;
while(l+1e-10<r){
mid=(l+r)/2;
if(check(mid))ans=mid,l=mid;
else r=mid;
}
printf("%.9lf\n",ans);
return 0;
}
题解
待续...