【题目记录】——ICPC南宁2017


题目集地址 ICPC南宁2017

F The Chosen One 数学规律

题目地址F - The Chosen One
题目大意:给出1-n,每次删除第2,4,6…位置上的数,问到最后剩下的数是谁?
思路:有一个规律,每次剩下的数都是不大于n的2的幂次,不过要用JAVA的大数来做
AC代码:

import java.math.BigInteger;
import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner input = new Scanner(System.in);
		int t = input.nextInt();
		String string;
		BigInteger TWO = new BigInteger("2");
		for(int i = 1;i<=t;i++)
		{
			string = input.next();
			BigInteger a = new BigInteger(string);
			if(a.equals(TWO))
			{
				System.out.println(2);
				continue;
			}
			BigInteger b = TWO;
			while(b.compareTo(a)<0)
			{
				b=b.multiply(TWO);
			}
			if(b.equals(a))
			System.out.println(b.toString());
			else {
				System.out.println(b.divide(TWO).toString());
			}
		}
	}
}

H The Game of Life 元胞自动机类问题

题目地址H - The Game of Life
题目大意:给出n*m的字符型矩阵,’.'表示不存在细胞,’#‘表示存在。

  • 细胞能够分裂的几种情况是:
  • 若当前活着的细胞旁边(8联通)存在的活着的细胞数小于2,则当前细胞死亡;
  • 若当前活着的细胞旁边存在的活着的细胞数等于2,则当前细胞能够进行到下一次的分裂;
  • 若当前活着的细胞旁边存在的活着的细胞数等于3,则当前位置新生成一个细胞;
  • 若当前的细胞旁边存在的活着的细胞数大于3,则当前细胞死亡;
    输出:在分裂321次过程中,第几次细胞的数目达到最大,数目最大是多少,和第321次细胞的数量
    思路:暴力模拟
    AC代码:
#include<bits/stdc++.h>
using namespace std;
int t,n,m;
int a[700][700],b[700][700];//相互作为临时数组
int getAround(bool flag,int x,int y) {//判断当前位置周围
    int sum=0;
    if(flag) {
        sum-=a[x][y];
        for(int i=-1; i<=1; i++)
            for(int j=-1; j<=1; j++)
                sum+=a[x+i][y+j];
    } else {
        sum-=b[x][y];
        for(int i=-1; i<=1; i++)
            for(int j=-1; j<=1; j++)
                sum+=b[x+i][y+j];
    }
    return sum;
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >>t;
    while(t--) {
        cin >>n>>m;
        memset(a,0,sizeof(a));//初始化
        memset(b,0,sizeof(b));//同上
        char ch;
        int sum=0,mx=0,pos=0,d=350,u=350+5,l=350,r=350+5;
        //初始化边界
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++) {
                cin >>ch;
                if(ch=='#')
                    a[350+i][350+j]=1,sum++,mx++;
            }
        for(int i=1; i<=321; i++) {
            sum=0;
            if(i&1) {
                for(int i=d-1; i<=u+1; i++)//只判断涉及到的范围
                    for(int j=l-1; j<=r+1; j++) {
                        int res=getAround(1,i,j);
                        b[i][j]=a[i][j];
                        if((res<2||res>3)&&a[i][j])b[i][j]=0;
                        else if(res==3&&!a[i][j])b[i][j]=1;
                        sum+=b[i][j];
                        if(b[i][j]) {//如果可以拓展
                            if(i==d-1)d--;
                            else if(i==u+1)u++;
                            if(j==l-1)l--;
                            else if(j==r+1)r++;
                        }
                    }
            } else {
                for(int i=d-1; i<=u+1; i++)//只判断涉及到的范围
                    for(int j=l-1; j<=r+1; j++) {
                        int res=getAround(0,i,j);
                        a[i][j]=b[i][j];
                        if((res<2||res>3)&&b[i][j])a[i][j]=0;
                        else if(res==3&&!b[i][j])a[i][j]=1;
                        sum+=a[i][j];
                        if(a[i][j]) {
                            if(i==d-1)d--;
                            else if(i==u+1)u++;
                            if(j==l-1)l--;
                            else if(j==r+1)r++;
                        }
                    }
            }
            if(sum>mx)pos=i,mx=sum;
        }
        cout <<pos<<" "<<mx<<" "<<sum<<endl;
    }
    return 0;
}

I Rake It In 对抗搜索

题目地址I - Rake It In
题目大意:有一个总分数,给出一共4×4矩阵,A和B对矩阵轮流操作,A先操作,每次操作选择一个2×2的矩阵,统计矩阵元素和加在总分数上,然后将这个2×2矩阵逆时针旋转90度,一共进行k轮,也就是2k次操作,A的目标是使得总分数最大,B的目标是使得总分数最小,计算两人在最优策略下最后获得的总分数。
思路:我们一开始思路错了,我们认为对于A来说每次操作选择最大的2*2的网格即可,B就选择最小的操作,这样就会遇到一个问题,比如在当下有好几个相同的最大值,应该操作哪一个?
正确思路应该是总共是先后手一共2×k轮,用DFS回溯,从最后一次后手起始加和,因为最后一次后手不影响其他的,一直回溯回去。

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
int maze[5][5],k;
int sum(int x,int y)
{
    return maze[x][y]+maze[x+1][y]+maze[x][y+1]+maze[x+1][y+1];
}
void clockwise(int x,int y)
{
    int t = maze[x][y];
    maze[x][y]=maze[x+1][y];
    maze[x+1][y]=maze[x+1][y+1];
    maze[x+1][y+1]=maze[x][y+1];
    maze[x][y+1]=t;
}
void anticlockwise(int x,int y)
{
    int t = maze[x][y];
    maze[x][y]=maze[x][y+1];
    maze[x][y+1]=maze[x+1][y+1];
    maze[x+1][y+1]=maze[x+1][y];
    maze[x+1][y]=t;
}
int dfs(int p)
{
    if(p==2*k)
    {
        int minn=INF;
        for(int i =1;i <=3;i++)
        {
            for(int j = 1;j <= 3;j++)
            {
                clockwise(i,j);
                minn=min(minn,sum(i,j));
                anticlockwise(i,j);
            }
        }
        return minn;
    }
    if(p&1)
    {
        int maxn=0;
        for(int i = 1;i <=3;i++)
        {
            for(int j =1; j<=3;j++)
            {
                anticlockwise(i,j);
                maxn = max(maxn,sum(i,j)+dfs(p+1));
                clockwise(i,j);
            }
        }
        return maxn;
    }
    else
    {
        int minn=INF;
        for(int i = 1;i <=3;i++)
        {
            for(int j =1; j<=3;j++)
            {
                anticlockwise(i,j);
                minn = min(minn,sum(i,j)+dfs(p+1));
                clockwise(i,j);
            }
        }
        return minn;
    }
}

int main()
{
//	freopen("in.txt","r",stdin);
//	int t = 1;
	int t;
	scanf("%d",&t);
	while(t--)
	{
	    scanf("%d",&k);
	    for(int i =1;i <= 4;i++)
        {
            for(int j = 1;j <=4;j ++)
            {
                scanf("%d",&maze[i][j]);
            }
        }
		printf("%d\n",dfs(1));
	}
    return 0;
}

J Rearrangement 数学规律

题目地址J Rearrangement
题目大意:给出2行n列的一个数组,完全重排列,问能否保证任意相邻的两个数的和不被3整除
思路:把所有的数字对3取余,然后就剩下3个数字,0,1,2,然后就是1和2不能放一起,0和0不能放一起,找规律即可,我们是按照1和2的个数是奇偶分类的,比较复杂,后面贴一个大佬的按照0的个数分类的代码。

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int N = 2e5+6;
int cnt[3];
void solve()
{
    for(int i = 0;i <= 2;i ++)
    {
        cnt[i]=0;
    }
    int n;
    scanf("%d",&n);
    for(int i =1;i <= 2*n;i++)
    {
        int t;
        scanf("%d",&t);
        cnt[t%3]++;
    }
    if(n==1)
    {
        if(cnt[0]==1)
        printf("YES\n");
        else if(cnt[1]==2||cnt[2]==2)
        {
            printf("YES\n");
        }
        else
            printf("NO\n");
    }
    else if(cnt[0]>n)
    {
        printf("NO\n");
    }
    else if((cnt[1]&1)&&(cnt[2]&1))
    {
        if(cnt[0]>=2)
        printf("YES\n");
        else
        {
            printf("NO\n");
        }
    }
    else if(!(cnt[1]&1)&&!(cnt[2]&1))
    {
        if(cnt[1]==0||cnt[2]==0)
        {
            printf("YES\n");
        }
        else if(cnt[0]>3)
        printf("YES\n");
        else
        {
            printf("NO\n");
        }
    }
    else
    {
        if(cnt[1]==0||cnt[2]==0)
        {
            printf("YES\n");
        }
        else if(cnt[0]>2)
        printf("YES\n");
        else
        {
            printf("NO\n");
        }
    }
}

int main()
{
//	freopen("in.txt","r",stdin);
//	int t = 1;
	int t;
	scanf("%d",&t);
	while(t--)
	{
		solve();
	}
    return 0;
}
//这是按照0分类的,比我们的简单多了
#include <bits/stdc++.h>
using namespace std;
int n;
int arr[3];
void input()
{
    scanf("%d",&n);
    CLR(arr, 0);
    for (int i = 0; i < 2 * n; i++)
        arr[read() % 3]++;
}
bool solve()
{
    if (arr[0] > n) return false;
    if (arr[0] <= 1 && arr[1] && arr[2]) return false;
    if (arr[0] == 2 && (arr[1] % 2 == 0) && (arr[2] % 2 == 0)) return false;
    return true;
}

int main()
{
    int t;
    scanf("%d",&t);
    while (t--)
    {
        input();
        puts(solve() ? "YES" : "NO");
    }
}

L Twice Equation 数学打表找规律

题目地址L - Twice Equation
题目大意:给出一个正整数L,找到一个最小的不小于L的整数n,使得存在一个正整数m满足等式2m(m+1)=n(n+1)
思路:我们是打表的 然后找到了一个规律a[i]=(a[i-1]+a[i-2])*5+4-a[i-3]

import java.math.BigInteger;
import java.util.Scanner;

public class Main {
	
	static BigInteger a[]= new BigInteger[2010];
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		init();
		Scanner inputScanner=new Scanner(System.in);
		int t = inputScanner.nextInt();
		for(int i = 1;i <= t;i++)
		{
			String string = inputScanner.next();
			BigInteger lBigInteger = new BigInteger(string);
			int l = 0;
			int r = 2000;
			if(lBigInteger.compareTo(a[l])<=0) {
				System.out.println(a[l].toString());
			}
			else {
				while(l<r)
				{
					int mid = (l+r)/2;
					if(a[mid].compareTo(lBigInteger)>=0)
					{
						r=mid;
					}
					else {
						l=mid+1;
					}
				}
				System.out.println(a[l].toString());
			}
		}
	}
	static void init() {
		a[0]=new BigInteger("3");
	    a[1]=new BigInteger("20");
	    a[2]=new BigInteger("119");
	    for(int i=3;i<=2000;i++)
	    {
	        a[i]=(a[i-1].add(a[i-2])).multiply(BigInteger.valueOf(5))
	        		.add(BigInteger.valueOf(4)).subtract(a[i-3]);
	    }
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值