音乐家观演问题编程求解(一)
音乐家观演问题描述参见:音乐家观演问题求解与拓展分析 和 音乐家观演问题通解初探
以下是一个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场演出时就会出现无法构造的问题。