「小组联考」第二周一次考试

「小组联考」第二周一次考试

  • T1 「SCOI2005」最大子矩阵
    • 题目
    • 考场思路
    • 题解
      • 方法一 朴素 d p dp dp
      • 方法二 分块思想
  • T2 「一本通 6.1 例 1」序列的第 k 个数
    • 题目
    • 考场思路即正解
  • T3 「雅礼国庆 2017 Day6」Star Way To Heaven
    • 题目
    • 考场思路
    • 题解


T1 「SCOI2005」最大子矩阵

题目

点这里

考场思路

刚开始看这道题,是看到它的数据范围,觉得又可以骗过一道题。
结果出来后,发现其实这道题是我得分最低的一道题…
考试的时候也想到 d p dp dp,结果最后 W A WA WA 了,只拿到 30 p t s 30pts 30pts
附个考场代码。

#include 
#include 
#include 
#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);
}

题解

方法一 朴素 d p dp dp

定义状态 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]:左边一列选到 i i i ,右边一列选到 j j j 时,一共选了 k k k 个矩形时的最大值。
其实,在这个状态定义下, m = 1 m=1 m=1 m = 2 m=2 m=2 的情况是可以同时解决的,在 m = 1 m=1 m=1 的时候,只需要将第二列当成全都是 0 0 0 的情况即可。
那么状态转移呢?我们分成四种情况讨论

  • 什么都不干, d p [ i ] [ j ] [ k ] = m a x { d p [ i − 1 ] [ j ] [ k ] , d [ i ] [ j − 1 ] [ k ] } dp[i][j][k]=max\{dp[i-1][j][k],d[i][j-1][k]\} dp[i][j][k]=max{dp[i1][j][k],d[i][j1][k]}
  • 只选左边的一列, d p [ i ] [ j ] [ k ] = m a x { d p [ i ] [ j ] [ k ] , d [ l − 1 ] [ j ] [ k − 1 ] + s [ i ] [ 1 ] − s [ l − 1 ] [ 1 ] ∣ l ∈ [ 1 , i ] } 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[l1][j][k1]+s[i][1]s[l1][1]l[1,i]}
  • 只选右边的一列, d p [ i ] [ j ] [ k ] = m a x { d p [ i ] [ j ] [ k ] , d [ i ] [ l − 1 ] [ k − 1 ] + s [ j ] [ 2 ] − s [ l − 1 ] [ 2 ] } dp[i][j][k]=max\{dp[i][j][k],d[i][l-1][k-1]+s[j][2]-s[l-1][2]\} dp[i][j][k]=max{dp[i][j][k],d[i][l1][k1]+s[j][2]s[l1][2]}
  • 选一个宽度为 2 2 2 的矩阵 ( ( ( 一定满足 i = j i=j i=j ) ) ) d p [ i ] [ j ] [ k ] = m a x { d p [ i ] [ j ] [ k ] , d p [ l − 1 ] [ l − 1 ] [ k − 1 ] s [ i ] [ 1 ] + s [ i ] [ 2 ] − s [ l − 1 ] [ 1 ] − s [ l − 1 ] [ 2 ] ∣ l ∈ [ 1 , i ] } 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]\} dp[i][j][k]=max{dp[i][j][k],dp[l1][l1][k1]s[i][1]+s[i][2]s[l1][1]s[l1][2]l[1,i]}

只需要再进行一些细节处理即可,时间复杂度 O ( n 2 ⋅ k ) O(n^2\cdot k) O(n2k)

#include
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;
}

方法二 分块思想

膜拜 J Z M JZM JZM 大佬,时间复杂度在 O ( n ⋅ k ) O(n\cdot k) O(nk) 左右,少了一个 n n n 的复杂度。
具体怎么做,等我有时间再写…


T2 「一本通 6.1 例 1」序列的第 k 个数

题目

点这里

考场思路即正解

一道打卡题,或者叫人口普查,不用过多解释…

#include
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

题目

点这里

考场思路

考场时差点想到正解,一道隐形的图论题。
首先考虑二分答案,然后用 d i j k s t r a dijkstra dijkstra 跑一遍,看能否有一条路能够连接上下边界。
如果能,说明我们这个答案过大了,否则,我们还可以再继续调大二分边界。
时间复杂度在 O ( k 2 ⋅ l o g 2 m ) O(k^2\cdot log^m_2) O(k2log2m) 左右,但是这个 l o g 2 m log^m_2 log2m 却被卡掉了。
结果最后 80 p t s 80pts 80pts
一气之下,我把 s p f a spfa spfa、双 b f s bfs bfs 都打了一遍,结果还是卡在 80 p t s 80pts 80pts 不动…

#include
#include
#include
#include
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;
}

题解

待续…

你可能感兴趣的:(「小组联考」第二周一次考试)