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;
}
——时间划过风的轨迹,那个少年,还在等你。