算法习题45:对于一个整数矩阵,存在一种运算,对矩阵中任意元素加一时,需要其相邻(上下左右)某一个元素也加一;;;一个整数数组,长度为n,将其分为m份,使各份的和相等,求m的最大值

来源:互联网 发布:淘宝上怎样搜索店铺 编辑:程序博客网 时间:2024/04/28 16:55
.雅虎:
1.对于一个整数矩阵,存在一种运算,对矩阵中任意元素加一时,需要其相邻(上下左右)某一个元素也加一,
现给出一正数矩阵,判断其是否能够由一个全零矩阵经过上述运算得到。
2.一个整数数组,长度为n,将其分为m份,使各份的和相等,求m的最大值
  比如{3,2,4,3,6} 可以分成{3,2,4,3,6} m=1; 
  {3,6}{2,4,3} m=2

  {3,3}{2,4}{6} m=3 所以m的最大值为3

----------------------------------------------------------------------

1:这题我我们自己试着模拟一遍,发现有点类似,扫雷,但是还是有差别的。

我想到了两个制约条件:

(1)某个非零元的值一定小于等于它上下左右的和

(2)整个矩阵的和必须是偶数(因为加1就跟着加另一个1,肯定是偶数)

似乎这两个条件就可以了,难道是必要条件???

我想不到从数学上证明,希望有人能给个清晰的答案。


2:这题我们应该逐步分解,

首先是平均分问题,我们可以分成几份呢?当然最多就是每个元素单独出来,所以最多n份;

其次,是不是1--n这么多都可以呢?不是的,必须总和能够整除份数才行

如果现在需要分成m份,而且能够被数组总和整除,那么就可以计算了,

接下来就是一个数组里找出某几个元素和为total/m 的序列了,

这里类似背包算法,需要用到递归

我这里就引用下面这篇博客代码了,写得比我整齐多了。。。

http://blog.csdn.net/peng_weida/article/details/7741888

#include <cstdio>#include <cstdlib>#define NUM8int maxShares(int a[], int n);//aux[i]的值表示数组a中第i个元素分在哪个组,值为0表示未分配//当前处理的组的现有和 + goal的值 = groupsumint testShares(int a[], int n, int m, int sum, int groupsum, int aux[], int goal, int groupId);int main(){//int a[] = {2, 6, 4, 1, 3, 9, 7, 5, 8, 10};//int a[]={2,2,3,5,6,6};int a[]={1,1,2,2,3,4,5,6};//打印数组值printf("数组的值:");for (int i = 0; i < NUM; i++)printf(" %d ", a[i]);printf("\n可以分配的最大组数为:%d\n", maxShares(a, NUM));return 0;}int testShares(int a[], int n, int m, int sum, int groupsum, int aux[], int goal, int groupId){if (goal < 0)return 0;if (goal == 0){groupId++;goal = groupsum;if (groupId == m+1)return 1;}for (int i = 0; i < n; i++){if (aux[i] != 0)continue;aux[i] = groupId;if (testShares(a, n, m, sum, groupsum, aux, goal-a[i], groupId))return 1;aux[i] = 0;//a[i]分配失败,将其置为未分配状态}return 0;}int maxShares(int a[], int n){int sum = 0;int *aux = (int *)malloc(sizeof(int) * n);for (int i = 0; i < n; i++)sum += a[i];for (int m = n; m >= 2; m--){if (sum%m != 0)continue;for (int i = 0; i < n; i++)aux[i] = 0;if (testShares(a, n, m, sum, sum/m, aux, sum/m, 1)){//打印分组情况printf("\n分组情况:");for (int i = 0; i < NUM; i++)printf(" %d ", aux[i]);free(aux);aux = NULL;return m;}}free(aux);aux = NULL;return 1;}



我的思路和上面差不多,不过上面这个方法借助辅助数组aux[]来表示被访问和分配组别这两个信息  这个技巧不错

而我却是用一个数组来记录当前累加的数字,而是否分配过则通过更改原数组值为0来实现

我的思路比较乱。。

#include <iostream>using namespace std;void Print(int *ans, int index);void H(int k, int sum, int n, int *arr, int dst, int *ans, int index);bool H2(int k, int sum, int n, int *arr, int dst, int *ans, int index, int* m);int main() {int ans[8];int index=0;int dst;//每组需要达到的和int i =0;int n = 8;//数组元素个数int m = n;int total = 24;//这里是数组的总和for(m=n;m>0;m--){int test[] = {1,1,2,2,3,4,5,6};int groupN = m;if(total%m == 0)dst = total/m;else continue;if(test[n-1]>dst)continue;cout<<"分成"<<m<<"组,"<<"每组和为"<<dst<<endl;for(i=n-1;i>=0;i--){ans[0] = test[i];index = 1;if(ans[0] == dst){Print(ans, index);groupN--;continue;}if(ans[0] > dst)continue;H2(0, ans[0], i, test, dst, ans, index, &groupN);}if(groupN != 0)cout<<"失败"<<endl;elsecout<<"成功"<<endl;cout<<"***************"<<endl;}return 0;}/* * k表示当前访问到第几个元素 * sum表示当前已累加的和 * arr表示原数组 * dst我们需要达到的和 * ans记录答案 * m 用来表示是否分组成功,每成功一组就减一,直至为0则成功 */bool H2(int k, int sum, int n, int *arr, int dst, int *ans, int index, int* m){int i = k;for(;i<n;i++){if(arr[i] == 0)continue;int temp = sum + arr[i];if(temp<dst){ans[index++] = arr[i];arr[i] = 0;if(!H2(i+1, temp, n, arr, dst, ans, index,m))return false;index--;arr[i] = ans[index];}else if(temp == dst){ans[index++] = arr[i];arr[i] = 0;Print(ans, index);(*m)--;index--;return false;}else{return true;}}return true;}void Print(int *ans, int index){int i = 0;for(i=0;i<index;i++)cout<<ans[i]<<" ";cout<<endl;}


我是打印出所有结果

分成4组,每组和为66 5 1 4 2 3 1 2 成功***************分成3组,每组和为86 1 1 5 3 4 2 2 成功***************分成2组,每组和为126 1 1 2 2 5 3 4 成功***************分成1组,每组和为246 1 1 2 2 3 4 5 成功***************



还是比较喜欢上面他的做法,这种记录方式很清晰

原创粉丝点击