音乐家观演问题编程求解(一)

音乐家观演问题描述参见:音乐家观演问题求解与拓展分析 和 音乐家观演问题通解初探

以下是一个console程序的实现代码:

AudPerf.h

 1 #ifndef _AUD_PERF_H
 2 #define _AUD_PERF_H
 3 
 4 #include <stdio.h>
 5 #include <stdint.h>
 6 #include <string>
 7 #include <vector>
 8 #include <set>
 9 #include <map>
10 #include <iostream>
11 #include <windows.h>
12 
13 typedef unsigned char ucell;
14 typedef unsigned int umult;
15 
16 struct AudSet {
17     std::vector<ucell> vecSpan;
18 };
19 
20 class AudPerfSlu
21 {
22 public:
23     ucell m_musiSum;
24     ucell m_showSum;
25     std::vector<AudSet> m_vecShow;
26         
27     AudPerfSlu(ucell sum) : m_musiSum(sum), m_showSum(2) {}
28     void reset() {m_vecShow.clear();}
29     bool tryShows(ucell val);
30     void calcPairs(std::map<ucell, std::set<ucell> >& mapPair);
31     umult calcPairSum();
32     bool condMet();
33     bool preCondMet();
34     bool buildNewShow();
35     bool genNextShow(AudSet& next, ucell aud = 0);
36     bool checkCond4NewShow(AudSet& next, ucell& aud);
37     void build1stShow();
38     void showDetail();
39     bool adjustShow();
40 };
41 
42 #endif

AudPerf.cpp

  1 #include "AudPerf.h"
  2 
  3 void appendVecBySpan(ucell shead, ucell stail, std::vector<ucell>& vec)
  4 {
  5     for (ucell idx = shead + 1; idx != stail; ++idx) {
  6         vec.push_back(idx);
  7     }
  8 }
  9 
 10 void fillMapPair(const std::vector<ucell>& vecAud, const std::vector<ucell>& vecPerf, std::map<ucell, std::set<ucell> >& mapPair)
 11 {
 12     for (ucell idxA = 0; idxA < vecAud.size(); ++idxA) {
 13         std::map<ucell, std::set<ucell> >::iterator it = mapPair.find(vecAud[idxA]);
 14         for (ucell idxP = 0; idxP < vecPerf.size(); ++idxP) {
 15             if (it == mapPair.end()) {
 16                 std::set<ucell> setPerf;
 17                 setPerf.insert(vecPerf[idxP]);
 18                 mapPair[vecAud[idxA]] = setPerf;
 19                 it = mapPair.find(vecAud[idxA]);
 20             }
 21             else {
 22                 it->second.insert(vecPerf[idxP]);
 23             }
 24         }
 25     }
 26 }
 27 
 28 void parseAudSet(const AudSet& audSet, ucell musiSum, std::vector<ucell>& vecAud, std::vector<ucell>& vecPerf)
 29 {
 30     ucell perfSpanHead = 0;
 31     ucell perfSpanTail = 0;
 32     for (size_t spanIdx = 0; spanIdx < audSet.vecSpan.size(); ++spanIdx) {
 33         perfSpanHead = perfSpanTail;
 34         perfSpanTail += audSet.vecSpan[spanIdx];
 35         vecAud.push_back(perfSpanTail);
 36         appendVecBySpan(perfSpanHead, perfSpanTail, vecPerf);
 37     }
 38     perfSpanHead = perfSpanTail;
 39     perfSpanTail = musiSum + 1;
 40     appendVecBySpan(perfSpanHead, perfSpanTail, vecPerf);
 41 }
 42 
 43 void AudPerfSlu::calcPairs(std::map<ucell, std::set<ucell> >& mapPair)
 44 {
 45     for (size_t idx = 0; idx < m_vecShow.size(); ++idx) {
 46         std::vector<ucell> vecAud;
 47         std::vector<ucell> vecPerf;
 48         parseAudSet(m_vecShow[idx], m_musiSum, vecAud, vecPerf);
 49         fillMapPair(vecAud, vecPerf, mapPair);
 50     }
 51 }
 52 
 53 umult AudPerfSlu::calcPairSum()
 54 {
 55     std::map<ucell, std::set<ucell> > mapPair;
 56     calcPairs(mapPair);
 57     umult pairSum = 0;
 58     std::map<ucell, std::set<ucell> >::iterator it = mapPair.begin();
 59     for (; it != mapPair.end(); ++it) {
 60         pairSum += it->second.size();
 61     }
 62     return pairSum;
 63 }
 64 
 65 bool AudPerfSlu::condMet()
 66 {
 67     return (calcPairSum() == m_musiSum * (m_musiSum - 1));
 68 }
 69 
 70 bool AudPerfSlu::preCondMet()
 71 {
 72     ucell shows = (ucell)m_vecShow.size();
 73     if (shows < 2)
 74         return true;
 75     umult pairSum = calcPairSum();
 76     umult pairsPerShow = (m_musiSum / 2) * (m_musiSum - m_musiSum / 2);
 77     bool ret = (pairSum + (m_showSum - shows) * pairsPerShow >= m_musiSum * (m_musiSum - 1));
 78     if (!ret)
 79         std::cout << " precond not met parsing " << (int)shows << " shows." << std::endl;
 80     return ret;
 81 }
 82 
 83 bool AudPerfSlu::adjustShow()
 84 {
 85     if (m_vecShow.size() <= 1)
 86         return false;
 87     AudSet next = m_vecShow[m_vecShow.size() - 1];
 88     if (genNextShow(next)) {
 89         m_vecShow[m_vecShow.size() - 1] = next;
 90         return true;
 91     }
 92     m_vecShow.pop_back();
 93     return adjustShow();
 94 }
 95 
 96 void AudPerfSlu::build1stShow()
 97 {
 98     AudSet oShow;
 99     ucell audSum = m_musiSum - (m_musiSum / 2);
100     for (ucell idx = 0; idx < audSum; ++idx) {
101         oShow.vecSpan.push_back(1);
102     }
103     m_vecShow.push_back(oShow);
104 }
105 
106 void calcSpanBorder(AudSet& show, ucell aud, ucell& border)
107 {
108     ucell level = 0;
109     for (ucell idx = 0; idx < show.vecSpan.size(); ++idx) {
110         level += show.vecSpan[idx];
111         if (level == aud) {
112             border = idx;
113             return;
114         }
115         if (level > aud)
116             break;
117     }
118 }
119 
120 // 12345,12346,...,56789
121 // 1234,...,6789
122 bool AudPerfSlu::genNextShow(AudSet& next, ucell aud)
123 {
124     AudSet cur = next;
125     if (cur.vecSpan[0] == m_musiSum + 1 - cur.vecSpan.size()) {
126         if (cur.vecSpan.size() == m_musiSum / 2) // 6789(bottom now)
127             return false;
128         /// from 56789 to 1234
129         ucell audSum = m_musiSum / 2;
130         next.vecSpan.clear();
131         for (ucell idx = 0; idx < audSum; ++idx) {
132             next.vecSpan.push_back(1);
133         }
134         return true;
135     }
136     ucell border = cur.vecSpan.size() - 1;
137     if (aud != 0)
138         calcSpanBorder(cur, aud, border);
139     for (ucell idx = border; idx >= 0; --idx) {
140         next.vecSpan[idx] = next.vecSpan[idx] + 1;
141         for (ucell ridx = idx + 1; ridx < cur.vecSpan.size(); ++ridx)
142             next.vecSpan[ridx] = 1;
143         ucell level = 0;
144         for (ucell cumuIdx = 0; cumuIdx < cur.vecSpan.size(); ++cumuIdx) {
145             level += next.vecSpan[cumuIdx];
146         }
147         if (level <= m_musiSum)
148             return true;
149     }
150     /// might get here when aud != 0
151     if (cur.vecSpan.size() == m_musiSum / 2)
152         return false;
153     ucell audSum = m_musiSum / 2;
154     next.vecSpan.clear();
155     for (ucell idx = 0; idx < audSum; ++idx) {
156         next.vecSpan.push_back(1);
157     }
158     return true;
159 }
160 
161 bool AudPerfSlu::checkCond4NewShow(AudSet& next, ucell& aud)
162 {
163     if (m_vecShow.size() <= 1) {
164         aud = 0;
165         return true;
166     }
167     std::map<ucell, std::set<ucell> > mapPair;
168     calcPairs(mapPair);
169     std::vector<ucell> vecAud;
170     std::vector<ucell> vecPerf;
171     parseAudSet(next, m_musiSum, vecAud, vecPerf);
172     std::map<ucell, std::set<ucell> >::iterator it;
173     for (ucell idx = 0; idx < vecAud.size(); ++idx) {
174         it = mapPair.find(vecAud[idx]);
175         if (it != mapPair.end()) {
176             if (it->second.size() == m_musiSum - 1) {
177                 std::cout << " New show with extra aud [" << (int)vecAud[idx] << "]." << std::endl;
178                 aud = vecAud[idx];
179                 return false;
180             }
181         }
182     }
183     aud = 0;
184     return true;
185 }
186 
187 bool AudPerfSlu::buildNewShow()
188 {
189     size_t curSum = m_vecShow.size();
190     if (curSum == 0) {
191         build1stShow();
192         return true;
193     }
194     AudSet next = m_vecShow[curSum - 1];
195     ucell aud = 0;
196     while (true) {
197         if (!genNextShow(next, aud))
198             return false;
199         if (checkCond4NewShow(next, aud))
200             break;
201     }
202     m_vecShow.push_back(next);
203     return true;
204 }
205 
206 bool AudPerfSlu::tryShows(ucell val)
207 {
208     ucell half = m_musiSum /2;
209     if (val * half * (m_musiSum - half) < m_musiSum * (m_musiSum - 1)) {
210         std::cout << " No need to try " << (int)val << " shows." << std::endl;
211         return false;
212     }
213     std::cout << " Start to try " << (int)val << " shows." << std::endl;
214     m_showSum = val;
215     reset();
216     while (true) {
217         if (m_vecShow.size() == m_showSum) {
218             if (condMet())
219                 return true;
220             if (!adjustShow())
221                 return false;
222             continue;
223         }
224         if (!preCondMet()) {
225             if (!adjustShow())
226                 return false;
227             continue;
228         }
229         if (!buildNewShow()) {
230             if (!adjustShow())
231                 return false;
232         }
233     }
234     return true;
235 }
236 
237 void printShow(const AudSet& oShow, ucell musiSum)
238 {
239     std::vector<ucell> vecAud;
240     std::vector<ucell> vecPerf;
241     parseAudSet(oShow, musiSum, vecAud, vecPerf);
242     for (ucell idx = 0; idx < vecAud.size(); ++idx)
243         std::cout << " " << (int)vecAud[idx];
244     std::cout << " -";
245     for (ucell idx = 0; idx < vecPerf.size(); ++idx)
246         std::cout << " " << (int)vecPerf[idx];
247     std::cout << std::endl;
248 }
249 
250 void AudPerfSlu::showDetail()
251 {
252     std::cout << "Total Shows: " << m_vecShow.size() << std::endl;
253     for (ucell idx = 0; idx < m_vecShow.size(); ++idx)
254         printShow(m_vecShow[idx], m_musiSum);
255 }
256 
257 int main()
258 {
259     printf("The sum of musicians please: ");
260     std::string strInput;
261     getline(std::cin, strInput);
262     umult raw = strtoul(strInput.c_str(), 0 ,10);
263     if (raw >= 255 || raw < 2) {
264         printf("\n The sum of musicians should be in [2,254].\n");
265         getline(std::cin, strInput);
266         return 0;
267     }
268     AudPerfSlu oSlu((ucell)raw);
269     ucell showSum = (raw > 6 ? 5 : 2);
270     DWORD tick = GetTickCount();
271     while (showSum <= raw) {
272         if (oSlu.tryShows(showSum)) {
273             oSlu.showDetail();
274             break;
275         }
276         ++showSum;
277     }
278     std::cout << " Time used(ms): " << GetTickCount() - tick << std::endl;
279     getline(std::cin, strInput);
280     return 0;
281 }

n=2,3,...,9的运行效果及分析 

////////////////
For n=2
Total Shows: 2
 1 - 2
 2 - 1
 Time used(ms): 16

////////////////
For n=3
Total Shows: 3
 1 2 - 3
 1 3 - 2
 2 3 - 1
 Time used(ms): 16

////////////////
For n=4
Total Shows: 4
 1 2 - 3 4
 1 3 - 2 4
 2 4 - 1 3
 3 4 - 1 2
 Time used(ms): 32

////////////////
For n=5
Total Shows: 4
 1 2 3 - 4 5
 1 4 5 - 2 3
 2 4 - 1 3 5
 3 5 - 1 2 4
 Time used(ms): 469

////////////////
For n=6
Total Shows: 4
 1 2 3 - 4 5 6
 1 4 5 - 2 3 6
 2 4 6 - 1 3 5
 3 5 6 - 1 2 4
 Time used(ms): 516

////////////////
For n=7
Total Shows: 5
 1 2 3 4 - 5 6 7
 1 2 3 5 - 4 6 7
 1 4 5 6 - 2 3 7
 2 4 5 7 - 1 3 6
 3 6 7 - 1 2 4 5
 Time used(ms): 18469

////////////////
For n=8
Total Shows: 5
 1 2 3 4 - 5 6 7 8
 1 2 5 6 - 3 4 7 8
 1 3 5 7 - 2 4 6 8
 2 3 5 8 - 1 4 6 7
 4 6 7 8 - 1 2 3 5
 Time used(ms): 221469
 
////////////////
For n=9
Total Shows: 6
 1 2 3 4 5 - 6 7 8 9 
 1 2 3 4 6 - 5 7 8 9
 1 2 5 6 7 - 3 4 8 9 
 1 3 5 6 8 - 2 4 7 9 
 2 3 5 6 9 - 1 4 7 8 
 4 7 8 9 - 1 2 3 5 6 
 Time used(ms): 102120142

n=2,3,4,5,6时,这个程序的求解是相当快的;但是n=7时,求解用了18秒;n=8时,用了200多秒;n=9时,则用了28小时,性能上的问题就很严重了。

本程序实现的一个基本思路就是对可能的观演安排组合做地毯式的构造和排查,这也是造成随着变量n值的增大,性能开销显著增长的原因。

程序实现上也做了一些降低性能开销的努力:

(1)每场演出都是u对v或v对u的,这里u = [m/2],v=m-u;

(2)第一场演出不参与调整;

(3)preCondMet :具体安排的前m场(m > 1)与剩余场次(m_showSum - m)的最大有序对数达不到要求时,不再构造第m+1场演出,而是对第m场演出进行调整;

(4)genNextShow :若前m场演出中,某位音乐家aud已经达到了观看所有其余音乐家演出的条件,而第m+1场演出中aud又以观众身份出现,则对第m+1场演出做调整,使得aud不再以观众身份出现。

 但实际效果并不理想。另外,(4)和(1)还会出现相互排斥的情形。如n=9时,如下的6场演出是满足要求的:

1 2 3 4 - 5 6 7 8 9

1 5 6 7 - 2 3 4 8 9

2 5 8 9 - 1 3 4 6 7

3 6 7 8 - 1 2 4 5 9

4 5 6 9 - 1 2 3 7 8 (或 4 6 9    - 1 2 3 7 8 5)

7 9       - 5 6           (或7          - 6)

但按上面的约束要求,在构造完前4场演出后,1、2、3、5、8已经观看了其余人的表演,为同时满足(1)和(4),则第5场演出只能为

4 6 7 9 - 1 2 3 5 8

再之后,构造第6场演出时就会出现无法构造的问题。

 

posted on 2021-03-21 22:57  readalps  阅读(135)  评论(0编辑  收藏  举报

导航