【浮*光】 #noip模拟# 2018.08.22

1.Game

【题目描述】

明明和亮亮在玩一个游戏。桌面上一行有n个格子,一些格子中放着棋子。

轮流选择如下方式中的一种移动棋子(图示中o表示棋子,*表示空格):

1) 当一枚棋子的右边是空格子的话,可以将这枚棋子像右移动一格。

**o***         ->           ***o**

2) 当一枚棋子的右边连续两个都有棋子,并且往右边数第3格没有棋子,

那么可以将这个棋子可以跳过那两个棋子。

**ooo*        ->           ***oo*

当任何一枚棋子到达最右边的格子时,这枚棋子自动消失。

当一方不能移动时,这方输。明明和亮亮都采取最优策略,明明先走,谁将取胜?

【输入数据】

第一行一个整数T表示数据组数, 0 < T < 10。

之后T组数据,每组两行,第一行n表示格子个数,

第二行n个字符表示每个格子的情况,o表示有棋子,*表示空着。

【输出数据】

对于每组数据一个输出,M表示明明赢,L表示亮亮赢。

【样例输入】

4

2

*o

5

*o***

6

**o**o

14

*o***ooo**oo**

【样例输出】

L

M

M

L

【数据范围】

0 <T < 10

对于50%的数据, n < 20。

对于100%的数据, n < 1000。 


【分析】

对于前50%的数据,由于n<20,整个棋盘的状态个数 < 2^20。

由于状态数有限,我们可以采取记忆化搜索的办法来实现。

但对于100%的数据,n的最大可能值达到999,记忆化搜索就不怎么可行了。

其实本题有一个更简单的做法:

考虑每个棋子到最右边格子的距离。把所有棋子这样的距离的总和计为s。

我们发现不管选择两种操作中的一种操作,每走一步,s的奇偶性都会发生一次变化。

所以说,如果第一次轮到明明时,s是奇数,那么每次轮到明明时s都是奇数。

而当s是奇数时,s肯定>0,这时明明总可以走最右边的棋子。

也就是说当s为奇数时,总有棋子可以走。所以说,一开始若s为奇数,则明明必胜。

同理,若一开始s为偶数,则当亮亮走的时候s总是奇数,所以明明必败。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

/*【Game】
桌面上一行有n个格子,一些格子中放着棋子。
明明和亮亮轮流选择、如下方式中的一种、移动棋子
(图示中o表示棋子,*表示空着的格子):
1)右边为空,向右移动。**o***  ->  ***o**
2)右边连续两个都有棋子,但右边第3格没有棋子,
那么可以将这个棋子可以跳过那两个棋子。 **ooo*  ->  ***oo* 
(原来最左边的那一颗已经到达最右边的格子,自动消失)
当一方不能移动时,这方输。都采取最优策略,明明先走,谁将取胜? */

//每一步,可以向右移动1或3格,奇偶性分析即可

char s[1109];

int main(){
    freopen("game.in", "r", stdin);
    freopen("game.out", "w", stdout);
    int T; scanf("%d",&T);
    while(T--){
        int n,sum=0;
        scanf("%d%s",&n,s); //格子数和情况
        for(int i=0;i<n;i++){
            if(s[i]=='o') sum+=n-1-i;
        }
        if(sum%2==0) printf("L\n");
        else printf("M\n"); 
    }
    return 0;
}

2.Walk

【题目描述】

有一块n *n 的土地上,明明和亮亮站在(1,1)处。每块地上写有一个数字a(i, j)。

每一秒钟,他们俩走向相邻且坐标变大的格子(从(x,y)到(x+1,y)或者从(x,y)到(x,y+1)),

他们俩可以按照不同方式来走,最后经过2n-1步到达(n,n)处。

明明和亮亮每一秒钟计算他们站的两个位置上数字的差的绝对值,

他们希望这些差值的和最大,请问这个最大的和是多少?

【输入数据】

第一行一个正整数n。

后面n行,每行n个整数,分别表示每块地上的数字。

【输出数据】

一个整数,表示最大的差值的和。

【样例输入】

4

1 2 3 4

1 5 3 2

8 1 3 4

3 2 1 5

【样例输出】

13

【数据范围】

n <= 100, 每块地上的数字的绝对值不超过300。


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

/*【walk】
有一块n*n的土地上,明明和亮亮站在(1,1)处。
每块地上写有一个数字a(i,j)。现在他们决定玩一个游戏,
每一秒钟,他们俩走向[相邻且坐标变大]的格子(从(x,y)到(x+1,y)或者从(x,y)到(x,y+1)),
他们俩可以按照不同方式来走,最后经过2n-1个格子到达(n,n)处。
明明和亮亮每一秒钟计算他们站的[两个位置上数字的差的绝对值],
他们希望这些差值的和最大,请问这个最大的和是多少? */

int s[109][109];
int f[227][109][109]; //斜线(行+列)+位置

int main(){
    freopen("walk.in", "r", stdin);
    freopen("walk.out", "w", stdout);
    int n; scanf("%d",&n);
    f[1][1][1]=0; //出发点
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            scanf("%d",&s[i][j]);
    for(int t=1;t<=n;t++) //(前半部分)每一斜层,t=i+j-1
        for(int a=1;a<=t-1;a++)
            for(int b=a+1;b<=t;b++){
                f[t][a][b]=f[t-1][a][b-1]; //a肯定小于t,b肯定>1
                if(a>1&&b<n) f[t][a][b]=max(f[t][a][b],f[t-1][a-1][b]); 
                if(a>1) f[t][a][b]=max(f[t][a][b],f[t-1][a-1][b-1]); 
                if(b<n) f[t][a][b]=max(f[t][a][b],f[t-1][a][b]); 
                f[t][a][b]+=abs(s[a][t+1-a]-s[b][t+1-b]);
            }
    for(int t=n+1;t<=2*n-2;t++)
        for(int a=1;a<=2*n-t-1;a++)
            for(int b=a+1;b<=2*n-t;b++){
                f[t][a][b]=max(f[t-1][a][b+1],f[t-1][a+1][b]); 
                f[t][a][b]=max(f[t][a][b],f[t-1][a+1][b+1]); 
                f[t][a][b]=max(f[t][a][b],f[t-1][a][b]); 
                f[t][a][b]+=abs(s[t+a-n][n-a+1]-s[t+b-n][n-b+1]);
            }
    printf("%d\n",f[2*n-2][1][2]);
    return 0;
}

3. Trip

【题目描述】

小朋友们出去郊游,明明和亮亮负责在草地上开一个篝火晚会。

这个草地你可以认为是又 N * M 块单位长度为1的小正方形的草组成。

显然有的地方草长的好,有的地方长的不好,每一块草都有一个舒服度 F。

现在明明和亮亮要选定一个 a*b 的草场作为晚会的地点,

小朋友们就坐在上面,显然他希望小朋友们坐的最舒服!

不过别急,篝火晚会怎么能少了篝火呢,篝火需要占用 c*d 的草地,

当然,篝火必须严格放置在选定的草地的内部,

也就是说,篝火的边界不能和选定操场的边界有公共部分。

给定 N*M 大草地每一块的舒服度,寻找一个 a*b 的草地,

除去一个严格内部的 c*d 的子草地,使得总的舒服度最大。

【输入数据】

第1行:6个整数,M ,  N,  b,   a,   d,   c。

第2~N+1行:每行 M 个整数,第 i行j列的整数 Fi,j 表示,第 i行j列的单位草地的舒服度。

【输出数据】

一个整数,表示最大的舒服值。

【样例输入】

8 5 5 3 2 1

1 5 10 3 7 1 2 5

6 12 4 4 3 3 1 5

2 4 3 1 6 6 19 8

1 1 1 3 4 2 4 5

6 6 3 3 3 2 2 2

【样例输出】

70

【数据说明】

下面的图片就是对样例的解释,阴影区域就是最佳的选择方案。

 

【数据范围】

1 ≤ Fi,j ≤ 100

3 ≤ a ≤ N

3 ≤ b ≤ M

1 ≤ c ≤ a-2

1 ≤ d ≤ b-2

对于 40% 的数据 N,M ≤ 10

对于 60% 的数据 N,M ≤ 150

对于 100% 的数据 N,M ≤ 1000


【法1】普通矩阵和+暴力

#include <cmath>
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

/*【trip】40%
N*M块单位长度为1的小正方形。每一块草都有一个舒服度F。
现在明明和亮亮要选定一个 a*b 的草场作为晚会的地点,
并且篝火必须【严格】放置在选定的草地的内部,需要占用 c*d 的草地。
给定每一块的舒服度,寻找一个 a*b 的草地,使得总的舒服度最大。
第一行:6个整数 M,N,b,a,d,c
第2~N+1行:每行 M 个整数,Fi,j 表示第i行j列的单位草地的舒服度。*/

const int maxn=1009;
int f[maxn][maxn],sum[maxn][maxn];
int m,n,b,a,d,c,ans=0;

int find_min(int xx,int yy,int x2,int y2,int c,int d){
//↑↑↑范围矩形内找矩形cd最小值
    int ans1=999999999;
    for(int i=xx+c-1;i<=x2;i++)
        for(int j=yy+d-1;j<=y2;j++)
            ans1=min(ans1,sum[i][j]-sum[i-c][j]-sum[i][j-d]+sum[i-c][j-d]);
    return ans1;
}

int find_max(int a,int b){
//↑↑↑范围矩形内找矩形ab最大值
    int ans1=0;
    for(int i=a;i<=n;i++)
        for(int j=b;j<=m;j++){
            int now1=sum[i][j]-sum[i-a][j]-sum[i][j-b]+sum[i-a][j-b];
            int now2=find_min(i-a+2,j-b+2,i-1,j-1,c,d);
            if(a>d) now2=min(now2,find_min(i-a+2,j-b+2,i-1,j-1,d,c));
            ans1=max(ans1,now1-now2);
        }
    return ans1;
}

int main(){
    freopen("trip.in", "r", stdin);
    freopen("trip.out", "w", stdout);
    scanf("%d%d%d%d%d%d",&m,&n,&b,&a,&d,&c);
    if(m<n) swap(m,n); //m>n
    if(b<a) swap(b,a); //b>a
    if(d<c) swap(d,c); //d>c
    memset(sum,0,sizeof(sum));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&f[i][j]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+f[i][j];
    ans=find_max(a,b);
    if(n>b) ans=max(ans,find_max(b,a));
    printf("%d\n",ans);
    return 0;
}

【法2】单调队列

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<vector>
#include<queue>
using namespace std;

/*【分析】求一个矩阵里的最小值。
然后行做一遍,列做一遍,更新答案。
我们可以一行一行的求出每个连续b-d-1个c*d矩形的最小值。
再基于这个最小值,一列一列的求出每个a*b大矩形中和最小的c*d矩形。
这样我们就可以找到最优的舒服值了。本算法的时间复杂度是O(MN)。*/

const int N=1100;
int n,m,A,B,C,D;
int a[N][N],c[N][N],s[N][N],t[N][N];
int p[N][N],rr[N][N],R[N][N];
struct node{ int x,d; }q[N*N];

void solve(){
    node k;
    int ind,l,r;
    for(int i=1;i<=n;i++){
        l=1;r=0;
        for(int j=1;j+D-1<=B-2;j++){
            k.x=j;k.d=p[i][j];
            while(q[r].d>k.d && l<=r) r--;
            q[++r]=k; ind=j;
        }
        for(int j=1;j+B-2<=m;j++){
            while(q[l].x<j) l++;
            rr[i][j]=q[l].d;
            ind++;k.x=ind;k.d=p[i][ind];
            while(q[r].d>k.d && l<=r) r--;
            q[++r]=k;
        }
    }
    for(int i=1;i<=m;i++){
        l=1;r=0;
        for(int j=1;j+C-1<=A-2;j++){
            k.x=j;k.d=rr[j][i];
            while(q[r].d>k.d && l<=r) r--;
            q[++r]=k; ind=j;
        }
        for(int j=1;j+A-2<=n;j++){
            while(q[l].x<j) l++; 
            R[j][i]=q[l].d;
            ind++;k.x=ind;k.d=rr[ind][i];
            while(q[r].d>k.d && l<=r) r--;
            q[++r]=k;
        }
    }
    int ans=0;
    for(int i=1;i+A-1<=n;i++)
        for(int j=1;j+B-1<=m;j++)
            ans=maxx(ans,t[i][j]-R[i+1][j+1]);
    printf("%d\n",ans);
}

int main(){
    freopen("trip.in","r",stdin);
    freopen("trip.out","w",stdout);
    scanf("%d%d%d%d%d%d",&m,&n,&B,&A,&D,&C);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&a[i][j]);
    memset(s,0,sizeof(s));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
    for(int i=1;i+A-1<=n;i++)
        for(int j=1;j+B-1<=m;j++)
            t[i][j]=s[i+A-1][j+B-1]-s[i+A-1][j-1]-s[i-1][j+B-1]+s[i-1][j-1];
    for(int i=1;i+C-1<=n;i++)
        for(int j=1;j+D-1<=m;j++)
            p[i][j]=s[i+C-1][j+D-1]-s[i+C-1][j-1]-s[i-1][j+D-1]+s[i-1][j-1];
    solve();
    return 0;
}

                                                 ——时间划过风的轨迹,那个少年,还在等你。

猜你喜欢

转载自blog.csdn.net/flora715/article/details/81943716