一道算法题

a_i_m_i_n 2019-04-24 11:50:41
一道类似凑数的算法题:
有未知数量m的一组子数字i【m】,将这些数字加起来之和,凑成数量n的另外一组母数字j【n】;
需要保证每一组单独的子数字之和<每一个母数字,且要保证(每一个母数字-它的子数集)差最小,最后一个凑的差值最大;
例如有一组子数字{3000,2000,800,900},凑成2个6000的母数字。需要{3000,2000,900}{800}凑成这两组子集。
母数字可以为相同的一个数字。且在一个母数字凑数集合里面,这些子数字是有顺序排列的,设一个变量阙值N,需要保证(j-N>减去最后一个数字的子集之和,j-N<加上最后一个数字的子集之和||子数集之和小于j-N将不考虑上面的情况);
例如上面所说的第一组子集{3000,2000,900}。假设阙值为1100,最后一个数字就不能为900.排列顺序可以为{2000,900,3000},{900,3000,2000}{3000,900,2000}等等都可以。
...全文
565 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
a_i_m_i_n 2019-05-30
  • 打赏
  • 举报
回复
验证了下。每加一个子数,运算等于增加很多倍。基本10个20个子数集就要运算很久了。实在太慢,感觉算法要优化
wdonghai 2019-05-30
  • 打赏
  • 举报
回复
结果对不对呢?
a_i_m_i_n 2019-04-29
  • 打赏
  • 举报
回复
好的,谢了,我验证一下
wdonghai 2019-04-27
  • 打赏
  • 举报
回复

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Collections;

namespace CP190427
{
public partial class Form1 : Form
{
private List<int> PData = new List<int>();//母数组
private List<int> CDataBak = new List<int>();//子数组(备份)
private List<int> CData = new List<int>();//子数组,凑过的数据会设置为0
private List<int> SData = new List<int>();//凑数时的子数组,每次都从CData里面取大于0的数据
private List<int> RSData = new List<int>();//结果数组
private List<int> BData = new List<int>();//回溯时存放数据
private List<int> Visit = new List<int>();//标记数组
private List<int> SData2 = new List<int>();//凑数后需要排列时的数组
private int PValue = 0;//凑数时的目数字
private int gCount = 0;//排列结果计数
private int gOST = 1100;//阙值
private bool gCheckOST;//是否需要检查阙值

public Form1()
{
InitializeComponent();
}

private void init()
{
PData.Clear();
PData.AddRange(new int[] { 0, 6000, 6000 });
//PData.AddRange(new int[] {0,6000,6000,5000});

CDataBak.Clear();
CDataBak.AddRange(new int[] { 0, 3000, 900, 2000, 800 });
//CDataBak.AddRange(new int[]{0,3000,900,2000,800,1200,4000,2500});
}

private void dfs(int step)
{
int i, sum=0, iRet;
for (i = 1; i <= step - 1; i++)
sum += BData[i];
if (sum>=PValue) return;
else if (sum < PValue)
{
iRet = PValue - sum;
if (iRet <= RSData[0])
{
RSData[0] = iRet;
for (i = 1; i <= step - 1; i++)
RSData[i] = BData[i];
}
}

for(i=1;i<SData.Count;i++)
if (Visit[i] == 0)
{
BData[step] = SData[i];
Visit[i] = 1;
dfs(step + 1);
Visit[i] = 0;
}
}

private void dfs2(int step)
{
int i;

if (step > SData2.Count-1)
{
StringBuilder sRs = new StringBuilder("{");
for (i = 1; i < BData.Count; i++)
sRs.Append(" " + BData[i].ToString());

if (gCheckOST)
{
if (BData[BData.Count-1] < gOST) return;
}

textBox1.AppendText(" "+sRs + " }\r\n");
gCount++;
return;
}

for(i=1;i<SData2.Count;i++)
if (Visit[i] == 0)
{
BData[step] = SData2[i];
Visit[i] = 1;
dfs2(step + 1);
Visit[i] = 0;
}

}

private void button1_Click(object sender, EventArgs e)
{
init();
//this.textBox1.Multiline = true;
this.textBox1.Clear();

CData.Clear();
CData.AddRange(CDataBak.ToArray());
int i, j, k, m;
for (i = 1; i < PData.Count; i++)
{
PValue = PData[i];
if (PValue <= 0) continue;
this.textBox1.AppendText(PValue.ToString() + "\r\n");
SData.Clear();
SData = CData.FindAll((v) => { return v > 0; });
if (SData.Count <= 0)
this.textBox1.AppendText("{}\r\n");
else
{
SData.Insert(0,0);
RSData.Clear();
BData.Clear();
Visit.Clear();
for (k = 0; k < SData.Count; k++)
{
RSData.Add(0);
BData.Add(0);
Visit.Add(0);
}

RSData[0] = PValue;
dfs(1);
SData2.Clear();
SData2 = RSData.FindAll((v) => { return v > 0; });
if (SData2.Count <= 0)
this.textBox1.AppendText("{}\r\n");
else
{
BData.Clear();
Visit.Clear();
BData.Add(0);
Visit.Add(0);
for (j = 1; j < SData2.Count; j++)
{
m = SData2[j];
BData.Add(0);
Visit.Add(0);
for(k=1;k<CData.Count;k++)
if (CData[k] == m)
{
CData[k] = 0;
break;
}
}

gCheckOST = RSData[0] < gOST;
gCount = 0;
dfs2(1);
if (gCount <= 0)
this.textBox1.AppendText("{}\r\n");
}
}
}

}
}
}

a_i_m_i_n 2019-04-26
  • 打赏
  • 举报
回复
说错,当差值大于阙值就不要要满足上述限制条件。我第一个帖子里面有说明。第二个子数集有800。6000-800=5200选大于阙值,而第一个子数集跟母数差值100,所以必须要满足最后一个子数的限制条件。我v 一八八零零三二三二五二 是数字的,可以详谈
a_i_m_i_n 2019-04-26
  • 打赏
  • 举报
回复
对于第二个子数集我有过说明。在帖子里面,当差值小于阙值就不需要要满足上面的条件了
wdonghai 2019-04-26
  • 打赏
  • 举报
回复
假如第一个母数字是6000,阙值是1100,从子数字{3000,2000, 800, 900}找出最接近6000的
3个数字[3000,2000,900],然后对这3个数字全排列并判断最后一个数字一定要大于阙值,得出
{900,2000,3000},{900,3000,2000},{3000,900,2000},{2000,900,3000}。
然后凑第二个母数字,将已经用过的子数字从原来最开始的子数字里面剔除,最开始的子数字
变成了{0,0,800,0},按楼主的意思是第二个母数字的子数组是{800},但是800是小于阙值1100的呀,
应该是第二个母数字没法凑才对。
a_i_m_i_n 2019-04-25
  • 打赏
  • 举报
回复
阙值就是给子数字集最后一个数字大小限定的一个范围,意思上面的例子,假如母数字是6000,阙值是1100,意思限制的界线在4900,意味着对于3000,2000,900这三个数字,最后一位排900是不合理的,因为5000不小于4900.如果把随意的2000,3000放在后面则不会有这种情况。前面的子数字集可以使无序排列。
a_i_m_i_n 2019-04-25
  • 打赏
  • 举报
回复
不是的 子数字之和必定要小于母数字6000,每次凑数的一次命令,母数字组的阙值都一样的,这个阙值是保证子数字最后一个数值的大小的,这个最后一个数字的大小要保证跨越阙值,以上面那个子数字集为例,阙值1100,界线就等于在6000-1100=4900的位置,{3000+900+2000=5900,3000+900=3900}{900+2000+3000=5900,900+2000=2900}.这两个数值组都在阙值界线4900两边 ,这就符合最后一个子数字大小满足的排列。这个子数集的排列就是合理的,前面两个或者更多的数字怎么排都没事。
wdonghai 2019-04-25
  • 打赏
  • 举报
回复
假如母数字是6000,没有阙值的话,就要求子数字之和小于6000,如果有阙值,比如1100的话,
就要求子数字之和小于6000-1000=4900?阙值是全局的还是每一个母数字都有一个阙值?
wdonghai 2019-04-24
  • 打赏
  • 举报
回复
阈值这个条件是不是指相邻两数之差呀。
题目中,
母数字可以为相同的一个数字。且在一个母数字凑数集合里面,这些子数字是有顺序排列的。
为何排列顺序可以为{2000,900,3000},{900,3000,2000}{3000,900,2000}等等都可以,
这几个子数字并不是有序的呀。


17,741

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 .NET Framework
社区管理员
  • .NET Framework社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧