文章标题

A题

题意

中文题,就不解释了

思路

回溯法直接搜就行了,当前行可以不放所以要加一句dfs(cur-1,num)

代码

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;
const int maxn = 20;
char maze[maxn][maxn];
bool vis[maxn];
int ans = 0;
int n,m;
void dfs(int cur,int num){
    if(num == 0){
        ans ++;
        return ;
    }
    if(cur < 0){
        return ;
    }
    for(int i = 0 ; i < n; i ++){
        if(vis[i] || maze[cur][i] != '#') continue;
        vis[i] = 1;
        dfs(cur-1,num-1);
        vis[i] = 0;
    }
    dfs(cur-1,num);//这一行不放的情况
}
int main(){
    while(~scanf("%d %d",&n,&m) && (n != -1 &&m != -1)){
        getchar();
        for(int i = 0 ; i < n ; i ++){
            for(int k = 0 ; k < n ; k ++){
                scanf("%c",&maze[i][k]);
            }
            getchar();
        }

        memset(vis, 0, sizeof vis);
        ans = 0;
        dfs(n-1,m);
        printf("%d\n",ans);
    }
}

B题

题意

给一个三维的迷宫,找出从S到E的最短路

思路

直接BFS搜就行了,只不过四方向改成六方向,其他都是一样的。

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>

using namespace std;
const int maxn = 35;
int mov[6][3]{{0,0,1},{0,0,-1},{0,1,0},{0,-1,0},{1,0,0},{-1,0,0}};
char maze[maxn][maxn][maxn];
bool vis[maxn][maxn][maxn];
int r,c,l;
struct Node{
    int x,y,z,step;
    Node(){};
    Node(int x,int y,int z, int step):x(x),y(y),z(z),step(step){};
};
queue<Node> Q;

inline bool isVaild(int x,int y,int z){
    if(x < 0 || x >= r) return false;
    if(y < 0 || y >= c) return false;
    if(z < 0 || z >= l) return false;
    if(vis[x][y][z])return false;
    if(maze[x][y][z] == '#') return false;
    return true;
}

int bfs(Node Start){
    memset(vis,0,sizeof vis);
    Q.push(Start);
    while(!Q.empty()){
        Node cur = Q.front();Q.pop();
        for(int i = 0 ; i < 6 ; i ++){
            int nx = cur.x + mov[i][0];
            int ny = cur.y + mov[i][1];
            int nz = cur.z + mov[i][2];
            if(isVaild(nx,ny,nz)){
                vis[nx][ny][nz] = 1;
                if(maze[nx][ny][nz] == 'E')
                    return cur.step+1;
                else{
                    Q.push(Node(nx,ny,nz,cur.step+1));
                    //printf("%d %d %d %d \n",nx,ny,nz,cur.step);
                }
            }
        }

    }
    return -1;
}
int main(){
    Node St;
    while(~scanf("%d %d %d", &l,&r,&c)&&(l+r+c)){
        getchar();
        for(int i = 0 ; i < l ; i ++){
            for(int j = 0 ; j < r ; j ++){
                for(int k = 0 ; k < c ; k ++){
                    scanf("%c",&maze[j][k][i]);
                    if(maze[j][k][i] == 'S'){
                        St.x = j;
                        St.y = k;
                        St.z = i;
                        St.step = 0;
                    }
                }
                getchar();
            }
            getchar();
        }
        int ans = bfs(St);
        if(ans > 0)
            printf("Escaped in %d minute(s).\n",ans);
        else
            puts("Trapped!");
        while(!Q.empty()) Q.pop();
    }
}

C题

题意

中文题,就不解释了

思路

看到最小值,自然想到BFS搜索,又要求找01串,所以很自然的想到在末尾加0和加1两种分支,按这个搜索,但是这里long long应该是存不下的,而且直接搜应该会T,所以要用余数来处理,mod N的余数相同的01串的只用记录第一次出现的那个就行了,后面的出现的直接抛弃。另外就是要记录一下前驱,用来最后输出01串。

代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5000 + 10;

struct Node {
    int flag;
    int val;
    int fa;
} node[maxn];

int vis[maxn];
int cnt;
int n;
queue<int>q;

void out(int p) {
    if(p == -1) return;
    out(node[p].fa);
    printf("%d", node[p].flag);
}

int bfs() {
    memset(vis, 0, sizeof(vis));
    node[0].fa = -1;
    node[0].flag = 1;
    node[0].val = 1;
    vis[1] = 1;
    q.push(0);
    while(!q.empty()) {
        int cur = q.front();
        q.pop();
        if(node[cur].val == 0) {
            return cur;
        }
        for(int i = 0 ; i < 2 ; i ++){
            node[cnt].flag = i; //当前值
            node[cnt].val = (node[cur].val * 10 + i) % n; //取模
            node[cnt].fa = cur; //前驱
            if(!vis[node[cnt].val]) {
                vis[node[cnt].val] = 1;
                q.push(cnt);
                cnt++;
            }
        }
    }
}


int main() {
    int T;
    cin >> T;
    while(T --) {
        cin >> n;
        if(n == 1) {
            cout << 1 << endl;
            continue;
        }
        cnt = 1;
        int ans = bfs();
        out(ans);
        puts("");
        while(!q.empty()) q.pop();
    }
}

D题

题意

给一个4位素数,每次可以变动某一位的数,使之变为另外一个素数,给出起始和目标素数,求最少的操作次数

思路

就是当时讲的那道例题的变形,只不过这限定在素数上,先打 一个10000以内的素数表,然后直接从起始开始BFS,在搜的时候判定一下搜到的数是不是素数就行了。

代码

#include <queue>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 1e4 + 10;
bool vis[maxn];

struct pri{
    int prim,step;
    int dig[4];
    pri(){};
    pri(int prim,int step):prim(prim),step(step){
        dig[0] = prim / 1000;
        dig[1] = prim % 1000 / 100;
        dig[2] = prim % 100 / 10;
        dig[3] = prim % 10;
    };
};
bool prime[maxn]= {0};

void IsPrime() { //筛法打素数表
    prime[0] = prime[1] = 0;
    prime[2] = 1;
    for(int i = 3; i < maxn; i++) {
        prime[i] = i % 2 == 0 ? 0 : 1;
    }
    for(int i = 3; i <= maxn; i++)
        if(prime[i])
            for(int k = i*i; k < maxn; k += i)
                prime[k] = 0;
}
queue<pri> Q;
int bfs(int start,int tag){
    Q.push(pri(start,0));
    vis[start] = 1;
    while(!Q.empty()){
        pri cur = Q.front();Q.pop();
        if(cur.prim == tag)
            return cur.step;
        for(int i = 0 ; i < 4 ; i ++){
            cur = pri(cur.prim,cur.step);
            for(int k = 0 ; k < 10 ; k ++){
                cur.dig[i] = k;
                int sum = cur.dig[0] * 1000 + cur.dig[1]*100+cur.dig[2]*10+cur.dig[3];
                if(prime[sum] && !vis[sum] &&sum > 1000){//合法性判定
                    vis[sum] =1;
                    Q.push(pri(sum,cur.step+1));
                }
            }
        }
    }
    return -1;
}

int main(){
    IsPrime();
    int T;
    cin >>T;
    while(T --){
        int s,t;
        cin >>s >>t;
        memset(vis,0,sizeof vis);
        int ans = bfs(s,t);

        if(ans >= 0)
            printf("%d\n", ans);
        else
            puts("Impossible");

        while(!Q.empty()) Q.pop();
    }
}

E题

题意

人在一个着火的矩阵中,火每个时间单位会向四个方向蔓延一格,人每个时间单位能跑一格,问能不能跑出去

思路

先搜一遍火,标记每个格子着火时间,然后在来搜人,如果人到达格子的时间快于火则加入队列中。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;
const int maxn = 1010;
int n, m;
char maze[maxn][maxn];
queue<pair<int, int> >q;
int fire[maxn][maxn];
int mov[][2] = {{0, 1}, {0, -1}, {1, 0}, { -1, 0}};
void bfsFire() {
    memset(fire, -1, sizeof(fire));
    while(!q.empty()) {
        q.pop();
    }
    for(int i = 0; i < n; i++)
        for(int k = 0; k < m; k++)
            if(maze[i][k] == 'F'){
                fire[i][k] = 0;
                q.push(make_pair(i, k));
            }

    while(!q.empty()) {
        pair<int, int> tmp = q.front();
        q.pop();
        int x = tmp.first;
        int y = tmp.second;
        for(int i = 0; i < 4; i++) {
            int nx = x + mov[i][0];
            int ny = y + mov[i][1];
            if(nx < 0 || ny < 0 || nx >= n || ny >= m) {
                continue;
            }
            if(fire[nx][ny] != -1) {
                continue;
            }
            if(maze[nx][ny] == '#') {
                continue;
            }
            fire[nx][ny] = fire[x][y] + 1;
            q.push(make_pair(nx, ny));
        }
    }
}
int man[maxn][maxn];
int bfsMan(){
    memset(man, -1, sizeof(man));
    while(!q.empty()) {
        q.pop();
    }
    for(int i = 0; i < n; i++)
        for(int k = 0; k < m; k++)
            if(maze[i][k] == 'J') {
                q.push(make_pair(i, k));
                man[i][k] = 0;
            }
    while( !q.empty() ) {
        pair<int, int> tmp = q.front();
        q.pop();
        int x = tmp.first;
        int y = tmp.second;
        if(x == 0 || y == 0 || x == n - 1 || y == m - 1) {
            return man[x][y] + 1;
        }
        for(int i = 0; i < 4; i++) {
            int nx = x + mov[i][0];
            int ny = y + mov[i][1];
            if(nx < 0 || ny < 0 || nx >= n || ny >= m) {
                continue;
            }
            if(man[nx][ny] != -1) {
                continue;
            }
            if(maze[nx][ny] == '#') {
                continue;
            }
            if(fire[nx][ny] != -1 && man[x][y] + 1 >= fire[nx][ny]) {
                continue;
            }
            man[nx][ny] = man[x][y] + 1;
            q.push(make_pair(nx, ny));
        }
    }
    return -1;
}
int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%d %d", &n, &m);
        for(int i = 0; i < n; i++) {
            scanf("%s", maze[i]);
        }
        bfsFire();
        int ans = bfsMan();
        if(ans == -1) {
            puts("IMPOSSIBLE");
        }
        else {
            printf("%d\n", ans);
        }
    }
    return 0;
}

F题

题意

中文题

思路

直接搜然后记录路径

代码

这题我没写。。。直接找的别人的代码。。。见谅。
他是用数组模拟的队列。

# include<stdio.h>
# include<stdlib.h>
int map[5][5];
int dir[4][2]={1,0,-1,0,0,1,0,-1};  //可走的四个方向
struct node{
       int x,y;
};
struct node queue[50],record[5][5];//queue记录可走的点,广搜;record记录改点的前驱
void bfs()   
{
       int head,tail,i;
       struct node cur,next;//cur为当前位置,next为下一个位置
       head=tail=0;
       cur.x=queue[tail].x;
       cur.y=queue[tail].y;
       tail++;
       while(head<tail)
       {
              cur=queue[head++];
              for(i=0;i<4;i++)
              {
                     next.x=cur.x+dir[i][0];
                     next.y=cur.y+dir[i][1];
                     if(next.x>=0&&next.y>=0&&next.x<5&&next.y<5&&map[next.x][next.y]==0)
                     {
                         //记录next的前驱,即next的坐标
                            record[next.x][next.y].x=cur.x;
                            record[next.x][next.y].y=cur.y;
                            if(next.x==4&&next.y==4)
                                   return ;
                            else
                            {
                                   map[next.x][next.y]=1;//标记走过
                                   queue[tail++]=next;
                            }
                     }
              }
       }
}
int main()
{
       int i,j,k,m,n;
       struct node cur;
       for(i=0;i<5;i++)
              for(j=0;j<5;j++)
                     scanf("%d",&map[i][j]);
       cur.x=0;
       cur.y=0;
       map[0][0]=1;
       queue[0]=cur;
       bfs();
       k=0;
       queue[k].x=4;
       queue[k++].y=4;
       i=j=4;
       while(i!=0||j!=0)//根据record的记录,从后往前回溯其路径,并存在queue中
       {
              m=i;n=j;
              i=record[m][n].x;
              j=record[m][n].y;
              queue[k].x=i;
              queue[k++].y=j;
       }
       for(i=k-1;i>=0;i--)//输出路径
              printf("(%d, %d)\n",queue[i].x,queue[i].y);
       return 0;
}

G题

题意

中文题

思路

回溯直接搜(貌似也可以递推)
注意处理一下如果当前行已经放了子的情况就行了

代码

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>

using namespace std;

long long sum = 0;
int arr[15];
int n;
void dfs (int cur){
    if (cur == n){
        sum ++;
        return;
    }

    int l = max(0,arr[cur-1] - 1);
    int r = min(n-1,arr[cur-1] + 1);

    if(arr[cur] == -1){ //当前行没初始的棋子
        for(int i = l ; i <= r ; i ++){
            arr[cur] = i;
            dfs(cur+1);
            arr[cur] = -1;
        }
    }
    else {//当前行有初始棋子
        if(arr[cur] >= l && arr[cur] <= r)
            dfs(cur+1);
        else
            return ;
    }
}

int main (){
    while (~scanf("%d",&n) && n){
        for ( int i = 0; i < n; i ++ )
        {
            scanf ("%d", arr+ i);
            arr[i]--; //从1~n转到0~n-1
        }
        sum = 0;
        if(arr[0]==-1){ //第一行
            for(int i = 0 ; i < n ; i ++){
                arr[0] = i;
                dfs(1);
                arr[0] = -1;
            }
        }
        else
            dfs(1);
        printf ("%lld\n", sum);
    }

    return 0;
}

H题

题意

给出一个矩阵@代表KFC,找一个KFC使它到和Y,M的最短距离之和最小

思路

从Y和M分别跑一次BFS求出他们到各个KFC的最短距离,然后找出到两点距离和最小的KFC就行了,注意有的kfc是无法到达的,要注意处理。

代码

#include <bits/stdc++.h>

using namespace std;

const int maxn = 300;
int ans = 1e9+7;
int n,m;
int f = 0;
bool vis[maxn][maxn];
char maze[maxn][maxn];
int kfc[maxn][maxn];
int mov[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};

struct Node{
    int x,y,step;
    Node(){};
    Node(int x,int y,int step):x(x),y(y),step(step){};
};
queue<Node> Q;

inline bool isVaild(int x,int y){
    if(x < 0 || x >= n) return false;
    if(y < 0 || y >= m) return false;
    if(vis[x][y])return false;
    if(maze[x][y] == '#') return false;
    return true;
}

int bfs(Node Start){
    memset(vis,0,sizeof vis);
    while(!Q.empty()) Q.pop();
    Q.push(Start);
    while(!Q.empty()){
        Node cur = Q.front();Q.pop();
        if(maze[cur.x][cur.y] == '@'){
            kfc[cur.x][cur.y] += cur.step;
            if(f && kfc[cur.x][cur.y]){ //第二次搜到的非零点才是合法的
                ans = min(ans,kfc[cur.x][cur.y]);
            }
        }
        for(int i = 0 ; i < 4 ; i ++){
            int nx = cur.x + mov[i][0];
            int ny = cur.y + mov[i][1];
            if(isVaild(nx,ny)){
                vis[nx][ny] = 1;
                Q.push(Node(nx,ny,cur.step+1));
            }
        }

    }
}

int main(){
    while(~scanf("%d %d", &n,&m)){
            memset(kfc,0,sizeof kfc);
        for(int i = 0 ; i < n ; i ++){
            scanf("%s", maze[i]);
        }
        ans = 1e9+7;
        f = 0;
        for(int i = 0 ;i < n ; i ++){
            for(int k = 0 ; k < m ; k ++){
                if(maze[i][k] == 'Y' || maze[i][k] == 'M'){
                    bfs(Node(i,k,0));
                    f++;
                }
            }
        }
        printf("%d\n",ans*11);
    }
    return 0;
}

I题

题意

有n个路口,从i到j路口之间的消耗是|i-j|,同时所有路口可以直接以1的耗费到达另外一个指定的路口。问从1到n的最小消耗。

思路

直接BFS搜图就可以了,每个点到他上一个点,下一个点,捷径直达的点建权为1的边。然后直接BFS搜即可

代码

#include <cstdio>
#include <algorithm>
#include <queue>

using namespace std;
int link[2000005] = {0};
int ans[2000005] = {0};
queue<pair<int,int> > Q;
int main(){
    int n;
    scanf("%d",&n);
    for(int i = 1 ; i <= n ; i ++){
        scanf("%d", &link[i]);
        ans[i] = n+10;
    }
    ans[1] = 0;
    Q.push(make_pair(1,0));
    while(!Q.empty()){
        pair<int ,int> to = Q.front();Q.pop();
        if(ans[link[to.first]] == n+10){ //捷径
            ans[link[to.first]] = to.second + 1;
            Q.push(make_pair(link[to.first] , to.second + 1));
        }
        if(ans[to.first +1]== n+10){ //向前
            ans[to.first +1] = to.second + 1;
            Q.push(make_pair(to.first+1 , to.second + 1));
        }
        if(to.first > 1){
            if(ans[to.first -1]!= n+10)continue;//向后
            ans[to.first -1] = to.second + 1;
            Q.push(make_pair(to.first-1 , to.second + 1));
        }
    }
    for(int i = 1 ; i <= n ; i ++)
        printf("%d ",ans[i]);
    return 0;
}

J题

题意

有4个贼,他们使用一个容量为n的背包依次去偷东西,他们偷的东西的数量为等比数列,即 a,aq,aq2,aq3 ,给出满足要求的方案数m,问n最小为多少。

思路

由题意有 ak3n 从而得到 ank3 ,很显然k不可能很大,所以可以二分n然后枚举k,即可得到对应a的数量,然后累加得到当前n所对应的方案数,和m进行比较,持续二分出结果,判断最后得到的n的方案数是否为m,是则输出n否则输出-1;
时间复杂度为 O(log(1e16)n3)

代码

#include <iostream>

using namespace std;
typedef long long ll;
ll cheak(ll x){
    ll ret = 0;
    for(ll i = 2 ; i*i*i <= x ; i ++){
        ret += x/(i*i*i); //计数
    }
    return ret;
}
int main(){
    ll n;
    cin >> n;
    ll l = 0 ,r = 1LL<<62;
    while(l < r){ //二分
        ll mid = (l+r) >> 1;
        if(cheak(mid) < n) l = mid +1;
        else r = mid;
    }
    if(cheak(l) == n) cout << l;
    else cout << -1;
}

K题

题意

给出一个矩阵,包含起点,墙壁,道路和宝藏,问得到所有宝藏的最短时间是多少

思路

最多只有四个宝藏,所以直接预处理出所有有效点(起点和宝藏)到其他所有有效点的最短距离,然后从起点开始dfs出最短路径即可。

代码

#include <bits/stdc++.h>

using namespace std;
const int maxn = 110;
struct edge{
    int to,cost;
    edge(int t,int c):to(t),cost(c){};
};

struct node{
    int x,y,ti;
    node(int x,int y,int t):x(x),y(y),ti(t){};
};

vector<edge> G[maxn]; //邻接表
char mat[maxn][maxn];
bool vis[maxn][maxn];
int loc[5][2];
int n,m,k;
int mov[5][2] = {{0,1},{0,-1},{1,0},{-1,0},{0,0}};
bool vi[6];
void bfs(int x ,int y,int idx){ //bfs处理出有效点之间的最短距离
    memset(vis,0,sizeof vis);
    queue<node> Q;
    Q.push(node(x,y,0));
    while(!Q.empty()){
        node cur = Q.front();Q.pop();
        for(int i = 0 ; i < 4 ; i ++){
            node next = node(cur.x + mov[i][0],cur.y + mov[i][1],cur.ti+1);
            if(next.x >0 && next.x <= n && next.y > 0 && next.y <= m && !vis[next.x][next.y] && mat[next.x][next.y] != '#'){
                vis[next.x][next.y] =true;
                Q.push(next);
                for(int i = 0 ; i <= k ; i ++){
                    if(loc[i][0] == x && loc[i][1] == y) continue;
                    if(next.x == loc[i][0] && next.y == loc[i][1])
                        G[idx].push_back(edge(i,next.ti)); //按最短距离连边
                }
            }
        }
    }
}

int dfs(int x, int len, int cnt){ //搜索最短路径
    if(cnt == k)
        return len;

    if(G[x].size() == 0){
        vi[x] = 0;
        return -1;
    }
    int ans = 1e9+7;
    for(int i = 0 ; i < G[x].size();i++){
        int to = G[x][i].to;
        if(!vi[to]){
            vi[to] = 1;
            ans = min(dfs(to,len + G[x][i].cost,cnt+1),ans); //更新最小值
            vi[to] = 0;
        }
    }
    return ans;
}
int main (){
    while(~scanf("%d %d", &n,&m)&&n&&m){
        for(int i = 1 ; i <= n ; i ++){
            scanf("%s",mat[i] + 1);
            for(int k = 0 ; k < m ; k ++)
                if (mat[i][k] == '@'){
                    loc[0][0] = i;
                    loc[0][1]= k;
                }

        }
        scanf("%d",&k);
        for(int i = 1 ; i <= k ; i ++){
            int x,y;
            scanf("%d %d", &x,&y);
            loc[i][0] = x;
            loc[i][1] = y;
        }
        for(int i = 0 ; i <=k ; i ++){
            G[i].clear();
            bfs(loc[i][0],loc[i][1],i);
        }
        vi[0] = 1;
        int ans = dfs(0,0,0);
        printf("%d\n",ans);
    }
}

L题

题意

中文题

思路

直接按行DFS搜索+最优化剪枝
这里用的剪枝条件是如果当前已经取的甜度加上剩下的各行中的最大的甜度,仍然比已经计算出的总和小,则剪去。

代码

#include <bits/stdc++.h>

using namespace std;
const int maxn = 20;
int candy[maxn][maxn];
int vis[maxn];
int max_sweet[maxn];
int ans;

void dfs(int cur,int all,int sum){
    if(cur < 0){
        ans = max(sum,ans);
        return ;
    }
    for(int i = 0 ; i <  all; i ++){
        if(vis[i]) continue;
        if(sum + max_sweet[cur] <= ans) continue; //最优化剪枝
        sum += candy[cur][i];
        vis[i] = 1;
        dfs(cur-1,all,sum);
        vis[i] = 0;
        sum -= candy[cur][i];
    }
}
int main(){
    int n;
    while(~scanf("%d",&n)){
        for(int i = 0 ; i < n ; i ++){
            int ma = 0;
            for(int k = 0 ; k < n ; k ++){
                scanf("%d",&candy[i][k]);
                ma = max(ma,candy[i][k]);
            }
            max_sweet[i] = ma;
        }
        for(int i = 1 ; i < n ; i ++)
            max_sweet[i] += max_sweet[i-1];

        ans = 0;
        dfs(n-1,n,0);
        printf("%d\n",ans);
    }
}

M题

题意

给出一个圆台的上下面半径和里面水的体积,求水的高度。

思路

二分水的高度然后判定体积就行了。

代码

#include <bits/stdc++.h>

using namespace std;
const double PI = acos(-1.0);
const double _exp = 1e-7;//最小误差

double calc(double r,double R,double h,double H){//计算体积
    double u = h/H*(R-r) + r;
    return PI/3*(r*r+r*u+u*u)*h;
}

int main(){
    int t;
    cin >>t;
    while(t--){
        double r,R,H,V;
        cin >>r >>R >>H >>V;

        double high=100;
        double low=0;
        while(high-low>_exp){
            double mid=(high+low)/2;
            double res =calc(r,R,mid,H);
            if(fabs(res-V)<=_exp)
                break;
            else if(res>V)
                high=mid;
            else
                low=mid;
        }
        cout <<fixed << low <<endl; //保留六位小数
    }
    return 0;
}

N题

题意

给定长宽的黑白棋棋盘摆满棋子,每次操作可以反转一个位置和其上下左右共五个位置的棋子的颜色,求要使用最少翻转次数将所有棋子反转为黑色所需翻转的是哪些棋子

思路

根据题目,每次操作都会影响到周围的“棋子”,而要使得每个1都被反转为0,那么我们就应当每次都反转1下方的棋子以改变1为0.

那么,当我们处理过1到n-1行的时候,前n-1行就都已经是0了,最后我们只需要检查最后一行是不是全部为0就可以检查这次的出操作是否正确了。如果正确且最小,那就存起来。最后输出,万事大吉。

当然,因为我们要改变第x行的1为0需要反转的是x+1行的位置。而这个整个规则是我们验证一组操作是否能达到目的所用的,那么我们需要在验证前先确定操作(没操作怎么验证..)。

于是根据规则内容可知,只需要能确认第一行的翻转情况,就能够推出下面所有的翻转情况并验证是否正确。于是需要做的就是枚举第一行的情况了。

代码

#include<stdio.h>  
#include<string.h>  
#include<algorithm>  
using namespace std;  

#define INF 300   
int dx[]={-1,0,0,0,1};  
int dy[]={0,1,0,-1,0};  

int map[15][15],flip[15][15],ans[15][15];  
int m,n;  

int get(int x,int y)//(x,y)的颜色  
{  
    int res=map[x][y];  
    for(int i=0;i<5;i++)  
    {  
        int a=x+dx[i],b=y+dy[i];  
        if(a>=0&&a<m&&b>=0&&b<n)  
        {  
            res+=flip[a][b];  
        }  
    }  
    return res&1;  
}  

int calc()//求出第一行确定的情况下的最小操作次数,  
{  
    for(int i=1;i<m;i++)  
        for(int j=0;j<n;j++)  
        {  
            if(get(i-1,j))//(i-1,j)是黑色,就要翻转  
            {  
                flip[i][j]=1;  
            }  
        }  

    for(int i=0;i<n;i++)//无解  
    {  
        if(get(m-1,i)) return INF;  
    }  

    int res=0;//翻转次数  
    for(int i=0;i<m;i++)  
        for(int j=0;j<n;j++)  
            res+=flip[i][j];  
    return res;  
}  
int main()  
{  
    while(~scanf("%d%d",&m,&n))  
    {  
        for(int i=0;i<m;i++)  
            for(int j=0;j<n;j++)  
                scanf("%d",&map[i][j]);  
        int res=INF;  
        memset(ans,0,sizeof(ans));  
        for(int i=0;i< 1<<n;i++)//枚举第一行的所有翻转情况  
        {  
            memset(flip,0,sizeof(flip));  
            for(int j=0;j<n;j++)  
            {  
                flip[0][j]= i>>j&1;//在i的状态下,第一行的j是否翻转  
            }  
            int temp=calc();  
            if(temp<res)  
            {  
                res=temp;  
                for(int i=0;i<m;i++)  
                    for(int j=0;j<n;j++) ans[i][j]=flip[i][j];  
            }  
        }  
        if(res==INF)  
        {  
            printf("IMPOSSIBLE\n");  
            continue;  
        }  
        for(int i=0;i<m;i++)  
            for(int j=0;j<n;j++)  
            {  
                printf("%d",ans[i][j]);  
                if(j!=n-1) printf(" ");  
                else   printf("\n");  
            }  
    }  
} 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值