枚举(矩阵消除、七段数码管)

本文探讨了两个编程竞赛题目,分别是矩阵消除游戏的最优策略和七段数码管的连通状态计数。对于矩阵消除,通过二进制枚举和排序找到最大得分;对于七段数码管,利用并查集和DFS判断所有可能的亮灯组合是否连通。文章详细阐述了解题思路和代码实现。
摘要由CSDN通过智能技术生成

目录

 

矩阵消除:

题目:

思路:

代码:

七段数码管(蓝桥杯题目)

大致题意:

思路:

 代码:


矩阵消除:

题目:

牛妹在玩一个名为矩阵消除的游戏,矩阵的大小是行列,第行第列的单元格的权值为,

牛妹可以进行个回合的游戏,在每个回合,牛妹可以选择一行或者选择一列,

然后将这一行或者这一列的所有单元格中的权值变为,同时牛妹的分数会加上这一行或者这一列中的所有单元格的权值的和。

牛妹想最大化她的得分,球球你帮帮她吧!

输入描述:

第一行三个整数

接下来行每行个整数表示矩阵中各个单元格的权值。

输出描述:

输出一个整数表示牛妹能获得的最大分数。

1<=n,m<=15 1<=k<=n*m

思路:

         刚开始想着记录每行每列的数,排序,选择,重排。显然,会造成相等情况下不同选择会有不同的结果。这个思路可以pass掉了。

         数据范围较小的时候,用二进制枚举:利用二进制枚举每一种状态。比如010101,就是选择1 3 5不选择2 4 6。在这里枚举那几行,然后重新排列再选择最高的几列(选择列的时候不会改变其它列的和)。

         枚举行数比k大或者枚举的行数+所有列数都达不到k,就没有必要再算下去,直接continue;

         K的值是≤n*m,需要缩小k的范围,赋值k,n,m的最小的那一样数(如果n,m小,k直接全部选择即可)

代码:

#include<algorithm>

#include<iostream>

#include<cstring>

#include<string>

#include<cstdlib>

#include<map>

#include<cmath>

#include<vector>



using namespace std;

typedef long long ll;

const int maxn = 1e6+50;

ll _map[20][20];

int main(){

         int n,m,k;

         cin >> n >> m >> k;

         ll ans = 0,sum_x[20]={0};

         for(int i = 1;i <= n;i++){

                  for(int j = 1;j <= m;j++)

                          cin >> _map[i][j],sum_x[i]+=_map[i][j];

         }

         k=min(k,n);k=min(k,m);

         int sta = (1<<n);

         for(int i = 0;i < sta;i++){

                  ll sum_y[20] = {0},maxs = 0,cnt_c=0;

                  for(int j = 1;j <= m;j++)

                          for(int k = 1;k <= n;k++)

                                   sum_y[j]+=_map[k][j];

                  for(int j = 0;j < n;j++){

                          if(((i>>j)&1)){

                                   cnt_c++;

                                   maxs += sum_x[j+1];

                                   for(int k = 1;k <= m;k++)

                                            sum_y[k]-=_map[j+1][k];

                          }

                  }

                  if(cnt_c+m < k||cnt_c > k)    continue;/*k<=n*m  过大的话可能导致cnt_c+m<k退出,极限等于min(m,n,k)即可*

                  sort(sum_y+1,sum_y+m+1);

                  if(cnt_c < k)

                          for(int j = m;j >= 1;j--){

                                   maxs+=sum_y[j];

                                   if(cnt_c+(m-j+1) == k) break;//错误 需要判断不需要列的情况

                          }               

                  ans = max(ans,maxs);

         }

         cout << ans << endl;

}

七段数码管(蓝桥杯题目)

大致题意:

七条边,不能全灭,有的亮有的灭或者全亮。所有亮的并且连通的种类数。

思路:

并查集+dfs,我感觉比较复杂,不如枚举每一种情况(毕竟只有7个管),然后判断是否

连通(可以利用Flody,离散中的沃舍尔算法)。

 代码:

#include<algorithm>

#include<iostream>

#include<cstring>

#include<string>

#include<queue>

#include<map>

#include<cstdio>

#include<cmath>

#include<stdlib.h>



using namespace std;

typedef long long ll;

const int INF = 0x3f3f3f3f;

const int maxn = 1e6+50;

int dis[10][10];

int a[10];

int tot;

void ini(){

         dis[1][1] = 0;     

         dis[1][2] = 1;      dis[2][2]=0;

         dis[1][3] = INF;dis[2][3]=1;  dis[3][3]=0; 

         dis[1][4] = INF;dis[2][4]=INF;dis[3][4]=1;     dis[4][4]=0; 

         dis[1][5] = INF;dis[2][5]=INF;dis[3][5]=INF;dis[4][5]=1;  dis[5][5]=0;

         dis[1][6] = 1;      dis[2][6]=INF;dis[3][6]=INF;dis[4][6]=INF;dis[5][6]=1;dis[6][6]=0;

         dis[1][7] = INF;dis[2][7]=1;  dis[3][7]=1;  dis[4][7]=INF;dis[5][7]=1;dis[6][7]=1;dis[7][7]=0;

         for(int i = 1;i <= 7;i++)

                  for(int j = i;j <= 7;j++)

                          dis[j][i] = dis[i][j];

}

void Floyd(){

         for(int k = 0;k < tot;k++)

                  for(int i = 0;i < tot;i++)

                          for(int j = 0;j < tot;j++)

                                   if(dis[a[i]][a[j]] > dis[a[i]][a[k]]+dis[a[k]][a[j]])

                                            dis[a[i]][a[j]] = dis[a[i]][a[k]]+dis[a[k]][a[j]];

}

bool check(int sta){

         int cnt = 1;tot = 0;

         while(sta){

                  if(sta&1==1)      a[tot++]=cnt;

                  sta>>=1;

                  cnt++;

         }

         Floyd();

         for(int i = 0;i < tot;i++)

                  for(int j = 0;j < tot;j++)

                          if(dis[a[i]][a[j]] == INF) return false;

         return true;

}

int main(){

         int sta = 1<<7,cnt=0;

         for(int i = 1;i < sta;i++){

                          ini();

                          if(check(i)) cnt++;

         }

         cout<<cnt<<endl;

         return 0;

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值