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


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

题解

待续...

posted @ 2020-01-18 17:44  Arextre  阅读(148)  评论(0编辑  收藏  举报