首页 > 试题广场 >

不要二

[编程题]不要二
  • 热度指数:23061 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解
二货小易有一个W*H的网格盒子,网格的行编号为0~H-1,网格的列编号为0~W-1。每个格子至多可以放一块蛋糕,任意两块蛋糕的欧几里得距离不能等于2。
对于两个格子坐标(x1,y1),(x2,y2)的欧几里得距离为:
( (x1-x2) * (x1-x2) + (y1-y2) * (y1-y2) ) 的算术平方根
小易想知道最多可以放多少块蛋糕在网格盒子里。

输入描述:
每组数组包含网格长宽W,H,用空格分割.(1 ≤ W、H ≤ 1000)


输出描述:
输出一个最多可以放的蛋糕数
示例1

输入

3 2

输出

4
//把所有的行和列排成一行(与原来等价) 那么问题就变成了将蛋糕每隔一个空放一个 可以多少
import java.util.*;
public class PutCakeNum{
  public static int deal(int r, int c){
      int n=0;
      if(r%4==0||c%4==0){n=r*c/2;}//如果能整除4 那么蛋糕个数为网格个数的一半 
      else{ n=r*c/2+1;}//不能被4整除 将蛋糕每隔一个空放一个 可以放多少 奇数的一半+1
      return n;
  }
  public static void main(String args[]){
      Scanner sc=new Scanner(System.in);
      while(sc.hasNext()){
          int r=sc.nextInt();
          int c=sc.nextInt();
          int res=deal(r,c);
          System.out.println(res);
      }
  }
}

编辑于 2017-08-24 21:53:53 回复(7)
### 第一种做法:
用贪心的思想来做,开始将棋盘map全置为1,1代表放入蛋糕。
从左向右从上到下遍历棋盘开始依此放蛋糕,然后将该块蛋糕上下左右欧几里得距离为2的点全部标记为0,意思为该点不能再放入蛋糕,如果下一步扫到的0,则跳过该点,如果扫到1,则ans++,继续把周围距离为2的点标记为0。扫完棋盘就A了。
因为w,h只有1000的大小,所以时间复杂度为100万。
我觉得肯定还可以用数学做,或者找规律做。

```
#include<iostream>
#include<algorithm>
using namespace std;
//greedy is good.
const int maxn = 1e3 + 5;
int map[maxn][maxn];
int main()
{
    int w,h,ans;
    while(cin>>w>>h)
    {
    for(int i=0;i<w;i++)            //初始化为1
            for(int j=0;j<h;j++)
                map[i][j]=1;
        ans=0;
        for(int i=0;i<w;i++)            //开始遍历棋盘
    {
        for(int j=0;j<h;j++)
        {
            if(map[i][j])            //如果该位置为1,则将上下左右距离为2的点标记为0
            {
                if((i-2)>=0) map[i-2][j]=0;
                if((i+2)<w)map[i+2][j]=0;
                if((j+2)<h)map[i][j+2]=0;
                if((j-2)>=0)map[i][j-2]=0;
                ans++;                //更新ans
            }
        }
    }  
    cout<<ans<<endl;      
    }
    return 0;
}
为什么可以贪心,因为肯定会放第一行第一列的点,并且肯定是越紧凑越多,这个好像不太严谨0.0
```
### 第二种做法
~~明天再做~~
果然找到了一个规律,可以通过第一种做法,将每次的摆法打印出来后的到的规律,当然打印出来后规律很明显。

![在这里插入图片描述](https://img-blog.csdn.net/20180925062723575?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ExMTIyMzMz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
可以发现,棋盘宏观上就是由若干个这个
1 1 0 0
1 1 0 0
0 0 1 1
0 0 1 1
循环体组成的。
然后实际上我们只要计算出w行h列的棋盘里所有的1的个数就好了。
然后这个题时间复杂度就成o(1)了。
我们可以将图分为四个部分,以(w,h)为(10 10) 为例子,可以分为四部分,分别取计算1的数量是多少。
11001100       11
11001100     11
00110011     00
00110011     00
11001100     11
11001100     11
00110011     00
00110011     00

11001100     11
11001100     11
 我们这四部分从左到右从上到下叫做a、b、c、d,它们的值表示的是每部分1的总量。
 a这个部分包含了(w/4)* (h/4)个完整的循环体,每个循环体值有8个1,所以a就算出来了。
 a=(w/4)*(h/4)*8;       
 bc同理通过观察可得,
 b=(w%4)*(h/4)*2;    
 c=(h%4)*(w/4)*2;
 d部分需要求出多少行和多少列,然后遍历在循环体中有多少个1,然后最多遍历16次算出。
 

```
#include<iostream>
#include<algorithm>
using namespace std;
int map[4][4]={1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1};
int main()
{
    int w,h;
    int a,b,c,d;
    while(cin>>w>>h)
    {
        a=(w/4)*(h/4)*8;        //第一部分
        b=(w%4)*(h/4)*2;        //第二部分
        c=(h%4)*(w/4)*2;         //第三部分
        d=0;                    //第四部分
        for(int i=0;i<(w%4);i++)
        {
            for(int j=0;j<(h%4);j++)
            {
                if(map[i][j])
                {
                    d++;
                }
            }
        }
        cout<<a+b+c+d<<endl;        
    }
    return 0;    
}
```
https://blog.csdn.net/q1122333/article/details/82833982
   

发表于 2018-09-25 07:32:12 回复(2)
//这个题画图可以知道,其实是循环的,每四行四列循环一次,因此整个盒子被分成四块区域,
//分别计算四块区域即可
import java.util.Scanner;
public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()){
            int w = sc.nextInt();
            int h = sc.nextInt();
            int sum = 0;
	    int w0 = w/4;
            int h0 = h/4;
            int w1 = w%4;
            int h1 = h%4;
            sum += w0*h0*8;
            sum += 2*w1*h0;
            sum += 2*h1*w0;
            if(w1<=2 && h1<=2){
                sum += w1*h1;
            }else if(w1==3 && h1==3){
                sum += 5;
            }else if(w1<=2 && h1==3){
                sum += 2*w1;
            }else if(w1==3 && h1<=2){
                sum += 2*h1;
            }
            
            System.out.println(sum);
        }
    }
}

发表于 2017-08-19 19:37:42 回复(1)

//package com.gulamjan.t009.不要二;
import java.util.Scanner;
public class Main {
 public static void main(String[] args) {   // TODO Auto-generated method stub   Scanner in = new Scanner(System.in);   while(in.hasNext()){    int W = in.nextInt();    int H = in.nextInt();    System.out.println(cakeNum(W, H));   }   in.close();  }  public static int  cakeNum(int W ,int H) {   int result = 0;   if (W % 4 == 0 || H % 4 == 0) {    result = W * H / 2;   }else {    result = W * H / 2 + 1;   }   return result;  } }

发表于 2017-10-04 00:20:57 回复(2)
import sys
x,y=list(map(int, sys.stdin.readline().split()))
if x % 4 == 0 or  y % 4 == 0:print(int(x*y//2))
elif x % 2 == 0 and y % 2 == 0:print(((x*y)//2+1) + 1)
else:print(x*y//2 + 1)

把图像画出来就规律就明显了
还有我感觉为什么py3每次都比py2运行时间长,都是同样的思路。

编辑于 2017-09-10 14:08:22 回复(1)
import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner in=new Scanner(System.in);
        int w=in.nextInt();
        int h=in.nextInt();
        int res=0;
        if(w%4==0||h%4==0)
            res=w*h/2;
        else
            res=w*h/2+1;
        System.out.println(res);
    }
}
发表于 2017-09-04 19:53:22 回复(4)
#include<iostream>
#include<cstdio>
using namespace std;
int main(){
    int len,wid;
    scanf("%d %d",&len,&wid);
    int sum=0;
    while(len>0&&wid>0){
        int len1=len/4;
        int len2=len%4;
        int wid1=wid/4;
        int wid2=wid%4;
        if(len2>1) len2=2;
        if(wid2>1) wid2=2;
        if(wid<2||len<2)           //注意特殊情况!!!
        sum+=(2*len1+len2)*(wid1*2+wid2);
        else sum+=(2*len1+len2)*(wid1*2+wid2)-(2*len1+len2-2)*(wid1*2+wid2-2);
        len-=2;
        wid-=2;
    }
    printf("%d",sum);
    return 0;
}
思路就是一圈一圈往里减少
发表于 2021-09-06 17:54:27 回复(0)
import java.util.*;
public class Main{
    static class Cake {
        int x;
        int y;
        boolean isFlag;
        public Cake(int x, int y, boolean isFlag) {
            this.x = x;
            this.y = y;
            this.isFlag = isFlag;
        }
        public void setFlag(boolean flag) {
            isFlag = flag;
        }
    }

    static int count = 0;
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int w = scanner.nextInt();
        int h = scanner.nextInt();
        count = w*h;
        List<List<Cake>> lists = new ArrayList<>();
        for (int i = 0; i < h; i++) {
            List<Cake> list = new ArrayList<>();
            for (int j = 0; j < w; j++) {
                list.add(new Cake(i,j,true));
            }
            list.add(null);
            list.add(null);
            lists.add(list);
        }
        lists.add(null);
        lists.add(null);
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                Cake curCake = lists.get(i).get(j);
                if (!curCake.isFlag ){
                    continue;
                }
                Cake rightCake = lists.get(i).get(j + 2);
                if (rightCake != null && rightCake.isFlag){
                    rightCake.setFlag(false);
                    count--;
                }
                List listLevel = lists.get(i + 2);
                if (listLevel != null ){
                    Cake topCake = lists.get(i + 2).get(j);
                    if (topCake.isFlag){
                        topCake.setFlag(false);
                        count--;
                    }
                }
            }
        }
        System.out.println(count);
    }
}

发表于 2020-03-09 12:36:59 回复(1)
import java.util.Scanner;

/*

思路:这个蛋糕要从第一个位置开始放。同时放蛋糕的情况是可以每四行一个循环,每四列一个循环。

我们可以先算出前面四行的情况,后边就是循环问题了,就都解答出来了。

其实前面四行中,前面两行的情况一样,后边两行情况也是相同的。所以就把前面两行区分为了1,2行和3,4行。

比如针对第一行:算出前面四列情况,后边就根据循环来算出这一行总的蛋糕数量。

那么第3,4行和第1,2行还有一个关系,就是sum12+sum34=纵向数量

*/ public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); while (sc.hasNext()) { int m = sc.nextInt(); int n = sc.nextInt(); int a = m / 4;// 横坐标除4取整 int b = m % 4;// 横坐标取余 int c = n / 4;// 纵坐标除4取整 int d = n % 4;// 纵坐标取余 int sum12 = 0;//前边两行放的蛋糕数量一样 int sum34 = 0;//3,4行蛋糕数量也一样 int sum = 0;最后的总的蛋糕数量 int sum1234 = 0;前边四行的蛋糕数量 // 先得出前四行分别的数目,首先前面两行一样,后边两行一样 // 首先是1,2两行放的蛋糕都一样 if (d == 1) { sum12 = c * 2 + 1; } else if (d == 0) { sum12 = (c - 1) * 2 + 2; } else { sum12 = c * 2 + 2; } // 然后是3,4两行,放的蛋糕都一样 sum34 = n - sum12; // 前面四行一共放蛋糕数量如下 sum1234 = 2 * (sum12 + sum34); // 前边计算了前面四行分别的数目,接下来是往纵看, if (b == 1) { sum = a * sum1234 + sum12; }else if (b == 2) { sum = a * sum1234 + 2 * sum12; }else if (b == 3) { sum = a * sum1234 + sum12 * 2 + sum34; } else { sum = a * sum1234; } System.out.println(sum); } } }

编辑于 2018-08-26 01:43:16 回复(0)
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000+10;
int done[maxn][maxn];
int main()
{
    int w,h;
    scanf("%d%d",&w,&h);
    memset(done,0,sizeof(done));
    for(int i=0;i<h;i++)
        for(int j=0;j<w;j++)
        {
            if(done[i][j]==0) {
                done[i][j]=1;
                done[i][j+2]=done[i+2][j]=-1;
            }
        }
    int solve=0;
    for(int i=0;i<h;i++)
        for(int j=0;j<w;j++)
            if(done[i][j]==1) solve++;
    cout<<solve<<endl;
    return 0;
}

发表于 2018-08-14 16:12:04 回复(0)
//这不是一个最优化问题,就是一个单纯的计数问题。也就是说不用分析,它的最优解只有一种情况。不妨设放蛋糕记为0,不放蛋糕记为-1。则最优化情况一定是从0开始。分析见图。程序来源牛客网友:大型火焰https://www.nowcoder.com/profile/5439431

#include <iostream>
using namespace std;
int main()
{
int a[1000][1000]={0};
int m,n,res=0;
cin>>m>>n;
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
if(a[i][j]==0)
{
res++;
if((i+2)<m)
a[i+2][j]=-1;
if((j+2)<n)
a[i][j+2]=-1;
}
cout<<res<<endl;
return 0;
}
编辑于 2018-07-23 13:15:05 回复(1)
#include<iostream>
using namespace std;
int main()
{
    int m, n;
    cin >> m >> n;
    int num1, num2, line1, line2;
        //算出两种模式,每行有多少块蛋糕
    if (m % 4 == 0) num1 = num2 = m / 2;
    if (m % 4 == 1) { num1 = m / 2 + 1; num2 = m / 2; }
    if (m % 4 == 2) { num1 = (m - 2) / 2 + 2; num2 = num1 - 2; }
    if (m % 4 == 3) { num1 = (m - 3) / 2 + 2; num2 = num1 - 1; }
    //算出这两种模式分别有多少行
    if (n % 4 == 0) { line1 = line2 = n/2; }
    if (n % 4 == 1) { line1 = n/2+1; line2 = n/2; }
    if (n % 4 == 2) { line1 = n/2+1; line2 =(n-2)/2 ; }
    if (n % 4 == 3) { line1 =(n-3)/2+2 ; line2 = line1-1; }
    //cout << num1 << "*" << line1 << "+" << num2 << "*" << line2 << "=";
    cout << num1*line1 + num2*line2;
    return 0;
}

发表于 2018-04-17 17:02:22 回复(0)
#include<stdio.h>


int main(){
    int w,h,n,i,j=0,flag=1,w1;
    scanf("%d%d",&w,&h);
    n = w*h;
    w1 = w;
    for(i=1;i<=h;i++){
        if(w<3&&h<3)break;
        if(flag==5) flag = 1;
        if(flag<3){
        if(w%2!=0){
            w++;
            if((w/2)%2==0){
                j = w/2-1;
            }else{
                j = (w-2)/2;
            }
        }else{
           if((w/2)%2==0){
                j = w/2;
            }else{
                j = (w-2)/2;
            }
        }
        }else{
           if(w%2!=0){
            w++;
            if((w/2)%2!=0){
                j = (w-2)/2+1;
            }else{
                j = w/2;
            }
        }else{
           if((w/2)%2!=0){
                j = (w-2)/2+2;
            }else{
                j = w/2;
            }
         }
        }
        flag++;
        n = n-j;
        j = 0;
        w = w1;
    }
    printf("%d",n);
    return 0;
}

发表于 2018-03-06 18:20:36 回复(0)
贪心的思想,建立二维数组,只要能放蛋糕,添加就往二维数组里面写进去1.最后1的个数就是蛋糕的个数
#include <iostream>
#include <stdio.h>
#include <vector>
using namespace std;

int main() {
int W, H;
while (cin >> W >> H) {
int count=0;
vector<vector<int> > grid(H,vector<int>(W,0));
for(int i=0;i<H; i++){
for(int j=0; j<W; j++){
if((i-2>=0 && grid[i-2][j]) || (j-2>=0 && grid[i][j-2])) grid[i][j]=0;
else {
grid[i][j]=1;
count++;
}
}
}
cout<< count<<endl;
}
}
编辑于 2018-03-05 09:39:24 回复(1)
复杂度有点大,但是感觉挺好理解的
#include <stdio.h>
int main(){
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF){
        bool mark[2000][2000];
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                mark[i][j]=false;
            }
        }
        int sum=0;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(mark[i][j]==true) continue;
                else{
                    sum++;
                    mark[i][j]=mark[i-2][j]=mark[i+2][j]=mark[i][j-2]=mark[i][j+2]=true;
                }
            }
        }
        printf("%d\n",sum);
    }
    return 0;
}

发表于 2018-02-20 17:00:58 回复(1)
所谓欧几里得距离不能等于二,因为行列都为整数,所以只可能是某一列坐标相等而行坐标相差2,或者行坐标相等而列坐标相差2。这道题其实是一个找规律的题,可以分四个部分分别求蛋糕数。第一部分:行列以4为单位,每个面积里有8块蛋糕(首先计算行列处以4的余数,计算整块面积,求出这一块总共多少块蛋糕)。第二部分求行方向除以4取模部分的蛋糕数,第三部分是列方向除以4取模部分的蛋糕数。第四部分是行列取模的交叉的地方。

#include<iostream>
using namespace std;
int main()
{
    int W,H;
    while(cin >> W >> H)
    {
       int divofW, divofH, modofW, modofH, cnt = 0;
       divofW = W / 4;
        divofH = H / 4;
        modofW = W % 4;
        modofH = H % 4;
        cnt += divofW * divofH * 2 * 4;

        cnt += modofW * 2 * divofH;

        cnt += modofH * 2 * divofW;

        if(modofW >= 3 && modofH >= 3)
            cnt += 5;
        else if(modofW >= 2 && modofH >=2)
            cnt += 4;
        else if((modofW == 2 && modofH ==1) || (modofW ==1 && modofH ==2))
            cnt += 2;
        else if(modofW >=1 && moofH >= 1)
            cnt += 1;
        cout << cnt << endl;
    }
    return 0;
}

发表于 2018-01-29 00:05:30 回复(0)
#include <bits/stdc++.h>

using namespace std;

int main()
{     int W,H,s;     while(cin>>W>>H)     {         if(W%4==0 || H%4==0)             s = W*H/2;         else if(W%2==0 && H%2==0)             s = (W*H/4 + 1)*2;         else             s = W*H/2 + 1;         cout<<s<<endl;      }     return 0;
}

发表于 2018-01-04 01:24:17 回复(0)
#include <stdio.h>
int main(){
    int w,h,res=0,i,j;
    scanf("%d%d",&w,&h);
    for(i=0;i<w;i++)
        for(j=0;j<h;j++)
            if(i%4<2&&j%4<2||i%4>1&&i%4<4&&j%4>1&&j%4<4) res++;
    printf("%d\n",res);
}

发表于 2017-10-21 16:56:46 回复(0)
import java.util.Scanner;

public class Main{
    public static void main(String[] args){
        // 填充法, 只要当前格没有蛋糕并且其四周间隔为2的位置也没蛋糕, 就在此格子上放个蛋糕并标记
        Scanner in = new Scanner(System.in);
        int w = in.nextInt();
        int h = in.nextInt();
        boolean[][] visited = new boolean[h][w];
        int count = 0;

        for (int i = 0; i < h; ++i) {
            for (int j = 0; j < w; ++j) {
                if(visited[i][j] == true)
                    continue;
                else{
                    if(j-2 < 0 || visited[i][j-2] != true){
                        if(j+2 >= w || visited[i][j+2] != true){
                            if(i-2 < 0 || visited[i-2][j] != true){
                                if(i+2 >= h || visited[i+2][j] != true){
                                    ++count;
                                    visited[i][j] = true;
                                }
                            }
                        }
                    }
                }
            }
        }
        System.out.println(count);
    }
}


发表于 2017-09-15 21:30:21 回复(0)
WAK头像 WAK
#include<iostream>

using namespace std;
int main()
{
    int arr[3][3]={1,2,2,2,4,4,2,4,5};
    int h,w;
    int num;
    cin>>w>>h;
    num = (w*h-(w%4)*(h%4))/2;
    if((w%4!=0)&&(h%4!=0))
        num += arr[w%4-1][h%4-1];
    else
        num = num;
    cout<<num<<endl;
        
    return 0;
}

其实除了右下角w和h被4除不开的余数组成的小矩形外,其他地方的能放蛋糕的和不能放的数量一样,先计算出这个数量num,差值就在最后的小矩形上。
1 2 2
2 4 4
2 4 5
小矩形最多3*3,最小没有,增加一个数组,根据余数情况,抽取小矩形中的能放蛋糕的数量,再加上前面的num,就是最后的数量。

发表于 2017-09-06 16:32:20 回复(0)