首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ Builder >

【讨论】【回帖有分】最优组合算法有关问题

2012-02-06 
【讨论】【回帖有分】最优组合算法问题!属性1属性2属性3A1001015B2002035C500515D1000323E300731F500318A、B、C、

【讨论】【回帖有分】最优组合算法问题!
属性1属性2属性3
A1001015
B2002035
C500515
D1000323
E300731
F500318

A、B、C、D、E、F....代表物品名称
属性1、2、3分别代表每个物品的一种属性

现在给定一个要求:从这些物品中取出一定数量的物品,要求它们属性1的总和小于X,属性2的总和小于Y,但是要保证属性3的总和是最大,应该如何取?将最优的结果列举出来,如果有多条最优可以分多条列举。

注:同一个物品可以取多个。

我这个只是一个例子,里面只随便列举了6个物品,实际情况下有300多个物品,希望能得到一个比较快点的算法。

大家踊跃讨论,回帖有分!
解决问题的重新开贴给分!

[解决办法]
占个位置,坐下慢慢看。
[解决办法]
从这些里取出一定数量n个物品
把属性1,属性2,属性3的数据分别放到3个TStringList里
对属性3的StringList用Sort()方法排序,从最后一个(也就是最大的那个)开始循环去n个,同时计算对应的X和Y 如果X,Y同时符合要求那取出这n个数据 如果不符合那继续在属性3的StringList里往上寻找n个


只是解决这个问题,没有考虑效率,慢慢想
[解决办法]
呵呵,我就看看不说话
[解决办法]
这要用到线性代数、矩阵运算。
[解决办法]
有点难度,得一定时间思考!!
[解决办法]
遗传算法解决吧,寻优问题,不是最值问题
[解决办法]
假设要取的个数为N=3, 属性1的总和小于X=1000, 属性2的总和小于Y=100
属性1 属性2 属性3 
A 100 10 15 
B 200 20 35 
C 500 5 15 
D 1000 3 23 
E 300 7 31 
F 500 3 18 
首先把上述数据按照属性3倒排序(这个可以放到数据库里去处理,也可以用常用的排序算法去处理)
处理结果如下
属性1 属性2 属性3 
200 20 35 
300 7 31 
1000 3 23
500 3 18 
100 10 15 
500 5 15 
对上述排序好的数据扩展,这个处理主要是考虑到每个物品可以重复,扩展的规则如下
1.每个物品重复的次数为0~N次 
2.每次重复后属性2的和不能超过X,属性3的合不能超过Y
根据规则上述数据变成如下数据
200 20 35 
200 20 35 
200 20 35
200 20 35 //这笔重复4次 
300 7 31 
300 7 31 
300 7 31 //这笔重复3次 
500 3 18 //这笔不重复,超过Y或X的直接不考虑 
100 10 15 
100 10 15 
100 10 15 
100 10 15 
100 10 15 
100 10 15 
100 10 15 
100 10 15 
100 10 15 //这笔重复9次 
500 5 15 //这笔不重复
然后循环上面的数据,每次取N组 直到 X的合计和Y的合计符合条件


[解决办法]
学习。。。
[解决办法]
先看看
[解决办法]
等我先看看离散数学
[解决办法]
听复杂,我感觉应该用运筹学的方法解决
[解决办法]

探讨
to Ring_Pt:
    谢谢,你的思路下面这部分我没看明白,能否详细说下?

---------------------
2.每次重复后属性2的和不能超过X,属性3的合不能超过Y
根据规则上述数据变成如下数据
200  20  35
200  20  35
200  20  35   
200  20  35    //这笔重复4次
300  7    31
300  7    31
300  7    31    //这笔重复3次
500  3    18    //这笔不重复,超过Y或X的直接不考虑
100  10  15
100  10  15
100  10  15
100  10  15
100  10  15
100  10  15
100  10  15
100  10  15
100  10  15    //这笔重复9次
500  5    15    //这笔不重复
然后循环上面的数据,每次取N组 直到 X的合计和Y的合计符合条件




[解决办法]
日 还没想出来 挺难弄的 等高手出现
[解决办法]
运筹学的理论很容易解决吧。
找个学数学的,建个模,
就不难解决了,
无非是解方程。
[解决办法]
算法复杂,容易死脑细胞
[解决办法]
太难了...
[解决办法]
对我来说很难
[解决办法]
本质就是一个搜索问题。
最简单的方法就是回溯,枚举出所有的情况,找出符合条件的就行。
[解决办法]
注:同一个物品可以取多个。

我理解没有到位。
好难!

[解决办法]
典型的背包问题嘛。帖子记住了,星期一回来帮你写代码。
[解决办法]
期待楼上的代码早日出现
[解决办法]
待楼上的代码早日出现
[解决办法]
穷举法
[解决办法]
我怎么看不明白题意啊,属性3最大的都比属性1最小的小,那怎么可能让“属性3的总和最大”呢?
[解决办法]
数学问题了
[解决办法]
看着怎么想01背包问题啊
[解决办法]
错了 没看见一个物品可以取多个 不能用背包的解法
[解决办法]
典型的线性规划问题!用300种物品举例。用lingo求解:
model:
!定义一个物品集,他的属性有x:该物品是否被使用。y:如果使用该物品,该使用多少件。a,b,c分别是三个属性。;
!X,Y为常数;
sets:
goods/goods1..goods300/:x,y,a,b,c;
endsets
@for(goods:@bin(x);@gin(y));
@sum(goods(i):x(i)*y(i)*a(i))<X;
@sum(goods(i):x(i)*y(i)*b(i))<Y;
max=@sum(goods(i):x(i)*y(i)*c(*));
data:
a,b,c=100 10 15 
200 20 35 
500 5 15 
1000 3 23 
300 7 31 
500 3 18 
!由于没有给出300种物品的属性,在这里只列举6种,必须加到300种;
enddata
end
[解决办法]
典型的线性规划,搜索一下:单纯形法,并不复杂
[解决办法]
回帖是一种美德!
[解决办法]
顶了在看。
[解决办法]
先更正一下
max=@sum(goods(i):x(i)*y(i)*c(i)); 
我用的是lingo语言,lingo是专门处理规划问题的工具。当然采用的是分支定界法和单纯型法。
用lingo处理最好。

[解决办法]
有点难度,好好学习一下
[解决办法]
thinking...
[解决办法]
可以用迭代法,可能不是最优的,但保证可以找到正确结果,伪代码:
C/C++ code
// 用堆栈stack来保存物品的组合bool IsOK(const stack& ); //判断 属性1之和<X and 属性2之和<Y  travel(stack &s){    for(int i=0; i<300; i++)    {        s.push(obj[i]);  //加入第i件物品        if(IsOK(s)) // 如果条件符合        {            setMaxCond(s); // 如果s里的属性3之和最大,就记录之              travel(s); // 递归,断续加入更多物品        }        s.pop(); //测试完成后取出前面加入的第i件物品(换下一件)    }}
------解决方案--------------------


探讨
可以用迭代法,可能不是最优的,但保证可以找到正确结果,伪代码:
C/C++ code// 用堆栈stack来保存物品的组合bool IsOK(const stack& );//判断 属性1之和<X and 属性2之和<Y
travel(stack&s)
{for(int i=0; i<300; i++)
{
s.push(obj[i]);//加入第i件物品if(IsOK(s))// 如果条件符合 {
setMaxCond(s);// 如果s里的属性3之和最大,就记录之 travel(s);// 递归,断续加入更多物品 }
s.pop();//测试完成后取出前面加入的第i件物品(换下一件) }
}

[解决办法]
属于组合数学的范畴吧
[解决办法]
hao wen ti
[解决办法]
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS
[解决办法]
mark先
[解决办法]
修改一下原来的伪代码,使用组合方式
C/C++ code
travel(stack &s, int start){    for(int i=start; i<300; i++)    {        s.push(obj[i]); //加入第i件物品        if(IsOK(s)) // 如果条件符合          {            travel(s, i); // 递归,断续加入更多物品         }        else //只有叠加到极限时才比较记录(前提是属性3没有负数)        {            s.pop();            setMaxCond(s); // 如果s里的属性3之和最大,就记录之         }        s.pop();    }}
[解决办法]
看了一下你的代码,IsOK可以改进一下
if(sumX > X || sumY > Y)
return false;
最好放到for的外面。

另外,传list的同时可以再传一个当前list的sumX和sumY,加入/减去一个物品时更新一下list的sumX和sumY(只要一次加/减运算即可),免去的每次都从用for从头加到尾,浪费时间。
[解决办法]

[解决办法]
我的代码:
C/C++ code
#include <iostream>#include <string>#include <vector>#include <iterator>using namespace std;struct TObj{    char name;    int prop1;    int prop2;    int prop3;};struct TGroup{    vector<int> vecObj;  // 物品组合(索引)    int nX;             // 属性1剩余点数    int nY;             // 属性2剩余点数    int nSumZ;          // 属性3总和};// 参数:物品数组objlist、物品数组大小n,临时组合grp,新加物品的起始索引i,属性3的最大组合maxgrpvoid travel(const TObj *objlist, const int n, TGroup &grp, int i, TGroup &maxgrp){    //下面两行输出所有组合,确认是否有重复组合出现    //copy(grp.vecObj.begin(), grp.vecObj.end(), ostream_iterator<int>(cout));    //cout << "\t nX: " << grp.nX << ";nY " << grp.nY << ";nSumZ: " << grp.nSumZ << endl;    for(; i<n; ++i)    {        const TObj & obj = objlist[i];        if(grp.nX >= obj.prop1 && grp.nY >= obj.prop2)// 剩余点数足够        {            // 加入当前物品索引            grp.vecObj.push_back(i);            grp.nX -= obj.prop1;            grp.nY -= obj.prop2;            grp.nSumZ += obj.prop3;            // 抽取最大组合            if(maxgrp.nSumZ < grp.nSumZ) maxgrp = grp;            // 继续尝试            travel(objlist, n, grp, i, maxgrp);            // 恢复            grp.vecObj.pop_back();            grp.nX += obj.prop1;            grp.nY += obj.prop2;            grp.nSumZ -= obj.prop3;        }    }}TObj g_objs[6]={    {'A',100,10,10},    {'B',200,21,21},    {'C',300,33,35},    {'D',400,42,48},    {'E',500,56,60},    {'F',600,65,81}};int main(int argc, char* argv[]){    // 临时组合初始值    TGroup grp;    grp.nX = 10000;    grp.nY = 1000;    grp.nSumZ = 0;    // 最大组合    TGroup maxgrp=grp;    travel(g_objs, 6, grp, 0, maxgrp);    // 输出结果    for(vector<int>::const_iterator itr=maxgrp.vecObj.begin();        itr!=maxgrp.vecObj.end();        ++itr)    {        cout << g_objs[*itr].name << ' ';    }    cout << endl << "属性1总和:" << 10000 - maxgrp.nX << endl;    cout << "属性2总和:" << 1000 - maxgrp.nY << endl;    cout << "属性3总和:" << maxgrp.nSumZ << endl;    // 暂停    cin.get();    return 0;} 


[解决办法]

[解决办法]
毛毛的代码也有点问题,当X,Y一样时会出现负数 

[解决办法]
囧~有一个不准确但速度很快的办法如果数量在10000+可以考虑
把属性三分别除以属性一,属性二之后用加权平均(根据xy的限制的倒数)算出一个值
按照这个值列表
从大到小依次排
在没达到xy的情况下从大到小依次添加遇到超过xy限制的跳过看下一个直到遍历
还可以做别的优化比如根据遍历后剩余的空间(xy)再更换物品

好吧其实我什么都不懂...咱是来打酱油

热点排行