2019.8.1

2019.8.1模拟B组

  1. 游戏 (Standard IO)
    Time Limits: 2000 ms Memory Limits: 262144 KB Detailed Limits
    Goto ProblemSet
    Description
    Alice和Bob在玩一个游戏,游戏是在一个NN的矩阵上进行的,每个格子上都有
    一个正整数。当轮到Alice/Bob时,他/她可以选择最后一列或最后一行,并将其删除,但
    必须保证选择的这一行或这一列所有数的和为偶数。如果他/她不能删除最后一行或最后一
    列,那么他/她就输了。两人都用最优策略来玩游戏,Alice先手,问Alice是否可以必胜?
    Input
    第一行:T,表示数据组数
    对于每组数据的第一行:N
    接下来N行,每行N个数,描述这个矩阵
    Output
    如果Alice必胜输出W,否则输出L
    Sample Input
    2
    2
    2 4
    6 8
    3
    5 4 2
    1 5 9
    7 3 8
    Sample Output
    L
    W
    100%数据满足
    1<=N<=1000
    保证每一行或每一列的和不会超过2
    10^9

思路:
简单博弈论,不需要用到sg函数。博弈论类题目与动态规划题目有一些类似,都是建立在递推上的刷表,对题目状态的遍历成一个有向无环图。对于这一类问题,我们需要把握起始的状态,转移的条件。我们将所有的状态分为必胜点和必败点两种。对于这个题目来说,我们可以发现只有一行,只有一列是初始的状态。我们按照游戏的反方向进行递推。我们预处理出这些状态。然后开始递推。每一个状态只能转移到它正下方一格和正右方一格的位置。转移的条件是当前行是偶数,并且转移前是必败。我们处理出了所有必胜的点,其余的都是必败的点。这里需要思考一个逻辑,能转移到必败点是必胜点的充要条件。
找到初态
转移的方式
转移的条件

#include<bits/stdc++.h>
using namespace std;
int t,n;
int a[1010][1010];
int k[1010][1010];
int sum[1010][1010];
int main(){
	scanf("%d",&t);
	while(t--){
		memset(k,0,sizeof(k));
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				scanf("%d",&a[i][j]);
				sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
			}
		}		
		k[1][1]=(!(a[1][1]%2));
		for(int i=1;i<=n;i++){
			if((sum[1][i]-sum[1][0])%2==0) k[1][i]=1;
		}
		for(int i=1;i<=n;i++){
			if((sum[i][1]-sum[0][1])%2==0) k[i][1]=1;
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if(i==1 && j==1) continue;
				if(k[i][j]) continue;
				if((i>1 && k[i-1][j]==0 && (sum[i][j]-sum[i-1][j])%2==0) || (j>1 && k[i][j-1]==0 && (sum[i][j]-sum[i][j-1])%2==0)){
					k[i][j]=1;
				}			
				else k[i][j]=0;	
			}
		}
		if(k[n][n]) cout<<"W"<<endl;
		else cout<<"L"<<endl;
	}	
}


Description
棋盘是由许多个六边形构成的,共有5种不同的六边形编号为1到5,棋盘的生成规
则如下:
1.从中心的一个六边形开始,逆时针向外生成一个个六边形。
.对于刚生成的一个六边形,我们要确定它的种类,它的种类必须满足与已生成的相
邻的六边形不同。
3.如果有多个种类可以选,我们选择出现次数最少的种类。
4.情况3下还有多个种类可以选,我们选择数字编号最小的。
现在要你求第N个生成的六边形的编号?
Input
第一行:T,表示数据组数
接下来T行,每行一个数:N,表示第N个六边形
Output
共t行,每行一个数,表示第N个数据的答案
Sample Input
4
1
4
10
100
Sample Output
1
4
5
5
Data Constraint
Hint
100%数据满足
1<=T<=20
1<=N<=10000

思路:
模拟。将六边形放到熟悉的邻接矩阵中,找到规律。我们可以将题目中的图形进行分层。对于找规律的题目我们要找到序列中的“循环节”,这样更能看清楚本质。

#include<bits/stdc++.h>
using namespace std;
int f[1100][1100];
struct node{
	int key;
	int num;
}p[10];
bool cmp(const node &a,const node &b){
	if(a.num==b.num) return a.key<b.key;
	return a.num<b.num;
}
int ans[1000100],k,n;
int now;
void in(int x,int y){
	sort(p+1,p+6,cmp);
	bool bk[10];
	memset(bk,0,sizeof(bk));
	bk[f[x-1][y]]=bk[f[x+1][y]]=bk[f[x][y-1]]=bk[f[x][y+1]]=bk[f[x-1][y-1]]=bk[f[x+1][y+1]]=1;
	for(int i=1;i<=5;i++){
		if(bk[p[i].key]==0){ 
			f[x][y]=p[i].key;
			p[i].num++;
			ans[now]=p[i].key;	
			break;
		}	
	}
	now++;
}

int main(){
	int x=250,y=250;
	for(int i=1;i<=5;i++)
		p[i].key=i;
	p[1].num=1;
	ans[1]=1;
	f[x][y]=1;
	now=2;
	k=1;
	while(now<10005){
		x++,y++;
		for(int i=1;i<=k;i++) x--,in(x,y);
		for(int i=1;i<=k;i++) x--,y--,in(x,y);
		for(int i=1;i<=k;i++) y--,in(x,y);
		for(int i=1;i<=k;i++) x++,in(x,y);
		for(int i=1;i<=k;i++) x++,y++,in(x,y);
		for(int i=1;i<=k;i++) y++,in(x,y);
		k++;
	}	
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int x;
		scanf("%d",&x);
		printf("%d\n",ans[x]);
	} 
}
  1. 数列 (Standard IO)
    Time Limits: 1000 ms Memory Limits: 262144 KB Detailed Limits
    Description
    给你一个长度为N的正整数序列,如果一个连续的子序列,子序列的和能够被K整
    除,那么就视此子序列合法,求原序列包括多少个合法的连续子序列?
    对于一个长度为8的序列,K=4的情况:2, 1, 2, 1, 1, 2, 1, 2 。它的答案为6,子序列
    是位置1->位置8,2->4,2->7,3->5,4->6,5->7。
    Input
    第一行:T,表示数据组数
    对于每组数据:
    第一行:2个数,K,N
    第二行:N个数,表示这个序列
    Output
    共T行,每行一个数表示答案
    Sample Input
    2
    7 3
    1 2 3
    4 8
    2 1 2 1 1 2 1 2
    Sample Output
    0
    6
    Data Constraint
    Hint
    100%数据满足

1<=T<=20

1<=N<=50000

1<=K<=1000000
序列的每个数<=1000000000

思路:
dp。cnt[i]表示以i为结尾的子串。确定了每一个i后(阶段),我们只需要找到与他能形成符合题意的开头j。我们将序列前缀和。ij能构成合法序列当且仅当sum[i],sum[j-1]在%k意义下同余。我们将每个sum的余数用一个桶装起来,f[j],
if(f([i]==f[j])cnt[i]++; if(f([i]==f[j]) cnt[i]++;if(f([i]==f[j])cnt[i]++;
易知,这样进行转移是既不重复也不遗漏的。

#include<bits/stdc++.h>
#define int long long
typedef long long ll;
using namespace std;
int t;
int k,n;
int a[50010],sum[50010],f[1000010];
int ok[500010],cnt[50010];
signed main(){
	scanf("%lld",&t);
	while(t--){
		memset(a,0,sizeof(a));
		memset(sum,0,sizeof(sum));
		memset(f,0,sizeof(f));
		memset(ok,0,sizeof(ok));
		memset(cnt,0,sizeof(cnt));
		scanf("%lld%lld",&k,&n);
		for(int i=1;i<=n;i++){
			scanf("%lld",&a[i]);
			sum[i]=sum[i-1]+a[i];
			ok[i]=sum[i]%k;
		}
		ll ans=0;
		f[0]=1;
		for(int i=1;i<=n;i++){
			ans+=f[ok[i]];
			f[ok[i]]++;
		}
		printf("%lld\n",ans);
	}
}

反思
看到一个题,突然卡住,懵B的情况很多。这种情况应该第一想法是,这个题考的是什么知识点。去往这个方向去想。比如说第二题,在没有思路的情况下发现这个题目没有考察算法。这时观察数据范围应该想到模拟。
不要去套模板。有的题目不如我所想的那么复杂。从题目本身出发而不是去想自己以前做过类似的题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值