听说这道题……很不好搞

zhaozhiqiang0124 2007-08-19 03:39:00
有一段钢筋长度不定

每次要在上面截取不同规格的钢筋,要求截取过后剩余部分为0或小于任何规格的长度 求出所有解
最多有200种不同长度的规格,但是每次使用几种规格不定,钢筋长度也不定 要求按照每次输入的规格和长度求出所有的解
大家有没有好的思路?
...全文
329 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
zhaozhiqiang0124 2007-08-20
  • 打赏
  • 举报
回复
只做循环的话,如果要进行200种长度规格的计算求解,那么就要嵌套200个循环,基本上就是没有希望。
我说的不定意思是说 每次手工输入的长度和需要的规格数量不定。比如说这次是5种规格求解,那么下次可能是8种规格求解,再下次还有可能是66种求解。不过唯一好的就是每次的规格总数都可以手工输入,每个规格的单个长度也可以输入,钢筋的总长度也可以手动输入。
lovingkiss 2007-08-20
  • 打赏
  • 举报
回复
针对特定的长度和条件,当然可以做循环。但是针对不定的长度和条件,你做一个看看~~

呵呵~~不是那么容易的。

==================================================================
博客空间:http://blog.csdn.net/lovingkiss
资源下载:http://download.csdn.net/user/lovingkiss
优惠接单开发,组件控件定制开发,成品源代码批发
联系方式:Q64180940 全天在线
==================================================================
fccfcc1234 2007-08-20
  • 打赏
  • 举报
回复
比如说钢筋的长度为10米,现在规格有3米 2.5米 1.5米三种类型,那么最后的解就有
3、0、0
2、1、1
2、0、2
1、2、1
1、1、3
1、0、4
0、4、0
0、3、1
0、2、3
0、1、5
两个嵌套循环可不可以啊,
double leave = 10;
for (int i = (int)(10 / 3); i >= 0; i--)
{
leave = leave - i * 3;
if (leave > 2.5)
{
for (int t = (int)(leave / 2.5); t >= 0; t--)
{
leave = leave - t * 2.5;
Console.WriteLine("{0} {1} {2}", i, t, (int)(leave / 1.5));
leave = leave + t * 2.5;
}
leave = leave - i * 3;
}
leave = 10;
}
0 0 6也可以的
sadever 2007-08-20
  • 打赏
  • 举报
回复
顶各位高手。
he_8134 2007-08-20
  • 打赏
  • 举报
回复
改了递归方式,这下好像没错了~~

class Program
{
static void Main(string[] args)
{
//new KindManger(10, new double[] { 1.5,2.5,3.5 });
new KindManger(30, new double[] { 5, 6, 7, 8, 9 });
}
}
struct Kind
{
public int Count;
public double Length;
public Kind(double length, int count) {
this.Length = length;
this.Count = count;
}
public override string ToString()
{
return string.Format("长度:{0},截取数量为{1}", Length, Count);
}
}
class KindManger {
double[] kinds_length;
double length;
public KindManger(double length, double[] kinds_length)
{
this.kinds_length = kinds_length;
this.length = length;
OutResult();
}
bool CheckSafe(double length) {
foreach (double l in kinds_length)
{
if (l <= length) return false;
}
return true;
}
void OutResult()
{
List<Kind[]> all = new List<Kind[]>();
Working(length, new Kind[] { }, kinds_length, all);
foreach (Kind[] ks in all)
{
foreach (Kind k in ks)
{
Console.WriteLine(k);
}
Console.WriteLine("--------------");
}
Console.WriteLine("共有{0}种方案!", all.Count);
Console.Read();
}
void Working(double length, Kind[] brothers,double[] kinds_length, List<Kind[]> all) {
double only = kinds_length[0];
if (kinds_length.Length == 1) {
if (!CheckSafe(length % only)) return;
Kind finnal = new Kind(only, (int)(length / only));
List<Kind> oneResult = new List<Kind>(brothers);
oneResult.Add(finnal);
all.Add(oneResult.ToArray());
return;
}
List<Kind> sub_kinds = new List<Kind>(brothers);
List<double> sub_lengths_tmp = new List<double>(kinds_length);
sub_lengths_tmp.Remove(only);
double[] sub_length = sub_lengths_tmp.ToArray();
int times = (int)(length / only);
for (int i = 0; i <= times; i++)
{
Kind add_brother = new Kind(only, i);
List<Kind> new_brother_tmp = new List<Kind>(brothers);
new_brother_tmp.Add(add_brother);
Kind[] new_brother = new_brother_tmp.ToArray();
Working(length - only * i, new_brother, sub_length, all);
}
}
}
lovingkiss 2007-08-20
  • 打赏
  • 举报
回复
就是类似背包的算法问题,基本上属于无解——所有的算法都是一个原理,绝无通用的可能。

用第归和循环,如果没有边界限定——有可能会让你的程序无限循环下去,至少是短时间内终止不了了。


贪心算法
贪心算法

一、算法思想

贪心法的基本思路:
--从问题的某一个初始解出发逐步逼近给定的目标,以尽可能快的地求得更好的解。当达到某算法中的某一步不能再继续前进时,算法停止。
该算法存在问题:
1. 不能保证求得的最后解是最佳的;
2. 不能用来求最大或最小解问题;
3. 只能求满足某些约束条件的可行解的范围。


实现该算法的过程:
从问题的某一初始解出发;
while 能朝给定总目标前进一步 do
   求出可行解的一个解元素;
由所有解元素组合成问题的一个可行解;

二、例题分析

1、[背包问题]有一个背包,背包容量是M=150。有7个物品,物品可以分割成任意大小。
要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。

物品 A B C D E F G
重量 35 30 60 50 40 10 25
价值 10 40 30 50 35 40 30


分析:

目标函数: ∑pi最大
约束条件是装入的物品总重量不超过背包容量:∑wi<=M( M=150)


(1)根据贪心的策略,每次挑选价值最大的物品装入背包,得到的结果是否最优?
(2)每次挑选所占空间最小的物品装入是否能得到最优解?
(3)每次选取单位容量价值最大的物品,成为解本题的策略。 ?


马踏棋盘的贪心算法
123041-23 XX

【问题描述】
马的遍历问题。在8×8方格的棋盘上,从任意指定方格出发,为马寻找一条走遍棋盘每一格并且只经过一次的一条路径。
【初步设计】
首先这是一个搜索问题,运用深度优先搜索进行求解。算法如下:
1、 输入初始位置坐标x,y;
2、 步骤 c:
   如果c>64输出一个解,返回上一步骤c--
(x,y) ← c
计算(x,y)的八个方位的子结点,选出那此可行的子结点
循环遍历所有可行子结点,步骤c++重复2
显然(2)是一个递归调用的过程,大致如下:
void dfs(int x,int y,int count)
{
int i,tx,ty;
if(count>N*N)
{
output_solution();//输入一个解
return;
}
for(I=0;i<8;++i)
{
tx=hn[i].x;//hn[]保存八个方位子结点
ty=hn[i].y;
s[tx][ty]=count;
dfs(tx,ty,count+1);//递归调用
s[tx][ty]=0;
}
}
这样做是完全可行的,它输入的是全部解,但是马遍历当8×8时解是非常之多的,用天文数字形容也不为过,这样一来求解的过程就非常慢,并且出一个解也非常慢。
怎么才能快速地得到部分解呢?
【贪心算法】
其实马踏棋盘的问题很早就有人提出,且早在1823年,J.C.Warnsdorff就提出了一个有名的算法。在每个结点对其子结点进行选取时,优先选择‘出口’最小的进行搜索,‘出口’的意思是在这些子结点中它们的可行子结点的个数,也就是‘孙子’结点越少的越优先跳,为什么要这样选取,这是一种局部调整最优的做法,如果优先选择出口多的子结点,那出口少的子结点就会越来越多,很可能出现‘死’结点(顾名思义就是没有出口又没有跳过的结点),这样对下面的搜索纯粹是徒劳,这样会浪费很多无用的时间,反过来如果每次都优先选择出口少的结点跳,那出口少的结点就会越来越少,这样跳成功的机会就更大一些。这种算法称为为贪心算法,也叫贪婪算法或启发示算法,它对整个求解过程的局部做最优调整,它只适用于求较优解或者部分解,而不能求最优解。这样的调整方法叫贪心策略,至于什么问题需要什么样的贪心策略是不确定的,具体问题具体分析。实验可以证明马遍历问题在运用到了上面的贪心策略之后求解速率有非常明显的提高,如果只要求出一个解甚至不用回溯就可以完成,因为在这个算法提出的时候世界上还没有计算机,这种方法完全可以用手工求出解来,其效率可想而知。
在前面的算法基础之上,增添一些程序加以实现:
函数1:计算结点出口多少
int ways_out(int x,int y)
{
int i,count=0,tx,ty;
if(x<0||y<0||x>=N||y>=N||s[x][y]>0)
return -1;//-1表示该结点非法或者已经跳过了
for(i=0;i<8;++i)
{
tx=x+dx[i];
ty=y+dy[i];
if(tx<0||ty<0||tx>=N||ty>=N)
continue;
if(s[tx][ty]==0)
++count;
}
return count;
}
函数2:按结点出口进行排序
void sortnode(h_node *hn,int n)//采用简单排序法,因为子结点数最多只有8
{
int i,j,t;
h_node temp;
for(i=0;i<n;++i)
{
for(t=i,j=i+1;j<n;++j)
if(hn[j].waysout<hn[t].waysout)
t=j;
if(t>i)
{
temp=hn[i];
hn[i]=hn[t];
hn[t]=temp;
}
}
}
函数3:修改后的搜索函数
void dfs(int x,int y,int count)
{
int i,tx,ty;
h_node hn[8];
if(count>N*N)
{
output_solution();
return;
}
for(i=0;i<8;++i)//求子结点和出口
{
hn[i].x=tx=x+dx[i];
hn[i].y=ty=y+dy[i];
hn[i].waysout=ways_out(tx,ty);
}
sortnode(hn,8);
for(i=0;hn[i].waysout<0;++i);//不考虑无用结点
for(;i<8;++i)
{
tx=hn[i].x;
ty=hn[i].y;
s[tx][ty]=count;
dfs(tx,ty,count+1);
s[tx][ty]=0;
}
}
函数4:主调函数
void main()
{
int i,j,x,y;
for(i=0;i<N;++i)//初始化
for(j=0;j<N;++j)
s[i][j]=0;
printf("Horse jump while N=%d\nInput the position to start:",N);
scanf("%d%d",&x,&y);//输入初始位置
while(x<0||y<0||x>=N||y>=N)
{
printf("Error! x,y should be in 0~%d",N-1);
scanf("%d%d",&x,&y);
}
s[x][y]=1;
dfs(x,y,2);//开始搜索
}


http://blog.csdn.net/cfreez/archive/2007/06/27/1668543.aspx
普遍意义的背包是求总价值最大,这里我只讨论能背的总重量最大。

为了便于理解,我通过一个具体的例子来说明

有物品重量为{16,13,9,6,7,1,3,18} 背包能装的最大重量为51

(1)先进行升序排列{1,3,6,7,9,13,16,18}

(2)求得相邻数的差 2 3 1 2 4 3 2

(3)对相邻数差分组并将得到的数按升序排列,于是有下结果

差为1 6-7

差为2 1-3 ,7-9, 16-18

差为3 3-6,13-16

差为4 9-13

(4)从最大的差组最大的数往下加,如果大于总重则跳过,直到到达最小值为止,并计算出其差值

得到数列为 13,9,16,6,3,18,7,1

13+9 +16+6+3+1=48 51-48=3差值为3

(5)找到小于或等于差值的组,从差最大的组找起,找到前缀在其中的数,并置换

在差为3组中找到13-16,3-6 但是16,6已在其中,于是往下找

在差为2组中找到16-18,替换

13 + 9 +18 + 6 +3 +1 =50 51-50=1

在差为1组中找到6-7,替换

13 + 9 +18 + 7 +3 +1 =50

结束

这种算法我称为“序差置换法”
==================================================================
博客空间:http://blog.csdn.net/lovingkiss
资源下载:http://download.csdn.net/user/lovingkiss
优惠接单开发,组件控件定制开发,成品源代码批发
联系方式:Q64180940 全天在线
==================================================================
he_8134 2007-08-20
  • 打赏
  • 举报
回复
做了一个~~~存在一个不影响结果的bug~~~

class Program
{
static void Main(string[] args)
{
new KindManger(10, new double[] { 3.5, 2.5, 1.5 });
}
}
struct Kind
{
public int Count;
public double Length;
public Kind(double length, int count) {
this.Length = length;
this.Count = count;
}
public override string ToString()
{
return string.Format("长度:{0},截取数量为{1}", Length, Count);
}
}
class KindManger {
double[] kinds_length;
double length;
public KindManger(double length, double[] kinds_length)
{
this.kinds_length = kinds_length;
this.length = length;
GetResule();
}
bool CheckSafe(double length) {
foreach (double l in kinds_length)
{
if (l <= length) return false;
}
return true;
}
void GetResule() {
bool suess;
Kind[][] all = Working(length, kinds_length, out suess);
if (suess)
{
foreach (Kind[] _array in all)
{
Console.WriteLine("-------");
for (int i = 0; i < _array.Length; i++)
{
Console.WriteLine(_array[i]);
if ((i + 1) % kinds_length.Length == 0) Console.WriteLine("-------");
}
}
}
else {
Console.WriteLine("得不到任何结果!");
}
Console.Read();
}
Kind[][] Working(double length, double[] kinds, out bool suess) {
double theParam = kinds[0];
if (kinds.Length == 1) {
suess = CheckSafe(length % theParam);
Kind[] tmp = new Kind[] { new Kind(theParam, (int)(length / theParam)) };
return new Kind[][] { tmp };
}
List<Kind[]> all = new List<Kind[]>();
List<double> _kinds = new List<double>(kinds);
_kinds.Remove(theParam);
double[] sub = _kinds.ToArray();
int times = (int)(length / theParam);
int trueCount=0;
for (int i = 0; i <= times; i++)
{
bool _suess;
Kind[][] sub_kinds = Working(length - theParam * i, sub, out _suess);
if (_suess)
{
trueCount++;
List<Kind> sub_all = new List<Kind>();
foreach (Kind[] ks in sub_kinds)
{
sub_all.Add(new Kind(theParam, i));
sub_all.AddRange(ks);
}
all.Add(sub_all.ToArray());
}
}
suess = trueCount > 0 ? true : false;
return all.ToArray();
}
}
cvsws 2007-08-20
  • 打赏
  • 举报
回复
使用动态编译和反射应该可以解决的吧~
yangcanwu1986 2007-08-20
  • 打赏
  • 举报
回复
一群數學專家
felix3118 2007-08-20
  • 打赏
  • 举报
回复
看运筹学去
jlzan1314 2007-08-20
  • 打赏
  • 举报
回复
没看懂.
greenery 2007-08-20
  • 打赏
  • 举报
回复
收藏先
lossfox 2007-08-20
  • 打赏
  • 举报
回复
用递归应该可以算出来吧
可以简单化问题
钢筋长度不限,假设10米
两种规格,假设4米,3米
先算最长规格的最大数量情况
4米的就是2,通过余数算第二种规格的最大数量为0
然后第一种数量减一开始循环,就是1
再算第二种规格的最大数量就是2
直道第一种数量为0
把规格数量,规格长度,剩余钢筋长度作为参数写一个方法的话,应该能求出来
只是乱说的,没试过



he_8134 2007-08-19
  • 打赏
  • 举报
回复
根本不知道在问什么~~~求的是材料还是规格?
he_8134 2007-08-19
  • 打赏
  • 举报
回复
就是说有一个数S(材料的长度)~~

有n1,n2,n3。。。。n200(代表不同规格的长度)

要求S除以任何n的余数小于任何n~~~
就好像说 S%n200 要大于任何其它n~~~

好像很难哟~~
wuyi8808 2007-08-19
  • 打赏
  • 举报
回复
0、0、6
runrunrun 2007-08-19
  • 打赏
  • 举报
回复
背包问题,世界性难题
liusong_china 2007-08-19
  • 打赏
  • 举报
回复
友情帮顶!
zhaozhiqiang0124 2007-08-19
  • 打赏
  • 举报
回复
比如说钢筋的长度为10米,现在规格有3米 2.5米 1.5米三种类型,那么最后的解就有
3、0、0
2、1、1
2、0、2
1、2、1
1、1、3
1、0、4
0、4、0
0、3、1
0、2、3
0、1、5
sunote 2007-08-19
  • 打赏
  • 举报
回复
没有看明白..

110,545

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

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