题目链接:https://vjudge.net/problem/UVA-207
题目翻译摘自《算法禁赛入门经典》
题目大意
你的任务是为PGA(美国职业高尔夫球协会)巡回赛计算奖金。巡回赛分为4轮,其中 所有选手都能打前两轮(除非中途取消资格),得分相加(越少越好),前70名(包括并列)晋级(make the cut)。所有晋级选手再打两轮,前70名(包括并列)有奖金。组委会事 先会公布每个名次能拿的奖金比例。例如,若冠军比例是18%,总奖金是$1000000,则冠军 奖金是$180000。
输入保证冠军不会并列。如果第 k 名有 n 人并列,则第 k~n+k - 1 名的奖金比例相加后平均分给这 n 个人。如果 n+k - 1 超过 70,则第 k~70 名的奖金比例相加后平均分给这 n 个人。奖金四舍五入到美分。所有业余选手不得奖金。例如,若业余选手得了第 3 名,则第 4 名会拿第 3 名的奖金比例。如果没取消资格的非业余选手小于70名,则剩下的奖金就不发了。
输入第一行为数据组数。每组数据前有一个空行,然后分为两部分。第一部分有 71 行 (各有一个实数),第一行为总奖金,第 i+1 行为第i名的奖金比例。比例均保留 4 位小数, 且总和为100%。第 72 行为选手数(最多144),然后每行一个选手。
业余选手名字后会有一个“*”。犯规选手在犯规的那一轮成绩为DQ,并且后面不再有其他成绩。但是只要没犯规,即使没有晋级,也会给出4轮成绩(虽然在实际比赛中没晋级的 选手只会有两个成绩)。输入保证至少有70个人晋级。
输出应包含所有晋级到后半段(make the cut)的选手。输出信息包括:选手名字、排 名、各轮得分、总得分以及奖金数。没有得奖则不输出,若有奖金,即使奖金是$0.00也要 输出,保留两位小数)。如果此名次至少有两个人获得奖金,应在名次后面加“T”。犯规选 手列在最后,总得分为DQ,名次为空。如果有并列,则先按轮数排序,然后按各轮得分之 和排序,最后按名字排序。两组数据的输出之间用一个空格隔开。
分析
代码如下
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define INIT() ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); 5 #define Rep(i,n) for (int i = 0; i < (n); ++i) 6 #define For(i,s,t) for (int i = (s); i <= (t); ++i) 7 #define rFor(i,t,s) for (int i = (t); i >= (s); --i) 8 #define ForLL(i, s, t) for (LL i = LL(s); i <= LL(t); ++i) 9 #define rForLL(i, t, s) for (LL i = LL(t); i >= LL(s); --i) 10 #define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i) 11 #define rforeach(i,c) for (__typeof(c.rbegin()) i = c.rbegin(); i != c.rend(); ++i) 12 13 #define pr(x) cout << #x << " = " << x << " " 14 #define prln(x) cout << #x << " = " << x << endl 15 16 #define LOWBIT(x) ((x)&(-x)) 17 18 #define ALL(x) x.begin(),x.end() 19 #define INS(x) inserter(x,x.begin()) 20 21 #define ms0(a) memset(a,0,sizeof(a)) 22 #define msI(a) memset(a,inf,sizeof(a)) 23 #define msM(a) memset(a,-1,sizeof(a)) 24 25 #define MP make_pair 26 #define PB push_back 27 #define ft first 28 #define sd second 29 30 template<typename T1, typename T2> 31 istream &operator>>(istream &in, pair<T1, T2> &p) { 32 in >> p.first >> p.second; 33 return in; 34 } 35 36 template<typename T> 37 istream &operator>>(istream &in, vector<T> &v) { 38 for (auto &x: v) 39 in >> x; 40 return in; 41 } 42 43 template<typename T1, typename T2> 44 ostream &operator<<(ostream &out, const std::pair<T1, T2> &p) { 45 out << "[" << p.first << ", " << p.second << "]" << " "; 46 return out; 47 } 48 49 inline int gc(){ 50 static const int BUF = 1e7; 51 static char buf[BUF], *bg = buf + BUF, *ed = bg; 52 53 if(bg == ed) fread(bg = buf, 1, BUF, stdin); 54 return *bg++; 55 } 56 57 inline int ri(){ 58 int x = 0, f = 1, c = gc(); 59 for(; c<48||c>57; f = c=='-'?-1:f, c=gc()); 60 for(; c>47&&c<58; x = x*10 + c - 48, c=gc()); 61 return x*f; 62 } 63 64 template<class T> 65 inline string toString(T x) { 66 ostringstream sout; 67 sout << x; 68 return sout.str(); 69 } 70 71 inline int toInt(string s) { 72 int v; 73 istringstream sin(s); 74 sin >> v; 75 return v; 76 } 77 78 typedef long long LL; 79 typedef unsigned long long uLL; 80 typedef pair< double, double > PDD; 81 typedef pair< int, int > PII; 82 typedef pair< int, PII > PIPII; 83 typedef pair< string, int > PSI; 84 typedef pair< int, PSI > PIPSI; 85 typedef set< int > SI; 86 typedef set< PII > SPII; 87 typedef vector< int > VI; 88 typedef vector< VI > VVI; 89 typedef vector< SI > VSI; 90 typedef vector< PII > VPII; 91 typedef map< int, int > MII; 92 typedef map< int, string > MIS; 93 typedef map< int, PII > MIPII; 94 typedef map< PII, int > MPIII; 95 typedef map< string, int > MSI; 96 typedef map< string, string > MSS; 97 typedef map< PII, string > MPIIS; 98 typedef map< PII, PII > MPIIPII; 99 typedef multimap< int, int > MMII; 100 typedef multimap< string, int > MMSI; 101 //typedef unordered_map< int, int > uMII; 102 typedef pair< LL, LL > PLL; 103 typedef vector< LL > VL; 104 typedef vector< VL > VVL; 105 typedef priority_queue< int > PQIMax; 106 typedef priority_queue< int, VI, greater< int > > PQIMin; 107 const double EPS = 1e-8; 108 const LL inf = 0x7fffffff; 109 const LL infLL = 0x7fffffffffffffffLL; 110 const LL mod = 1e9 + 7; 111 const int maxN = 1e4 + 7; 112 const LL ONE = 1; 113 const LL evenBits = 0xaaaaaaaaaaaaaaaa; 114 const LL oddBits = 0x5555555555555555; 115 116 struct Player{ 117 string name; 118 int rank = 0, sc[4], totS = 0; 119 bool isAmateur = false, isTied = false, isDQ = false, hasMoney = false; 120 double bonus = 0; 121 122 Player() {} 123 Player(string s) { 124 string tmp; 125 name = s.substr(0, 20); 126 if(name.find('*') != string::npos) isAmateur = true; 127 128 Rep(i, 4) sc[i] = 666666; // 方便排序 129 130 Rep(i, 4) { 131 tmp = s.substr(21 + i * 3, 2); 132 if(tmp == "DQ") { 133 isDQ = true; 134 break; 135 } 136 else sc[i] = toInt(tmp); 137 } 138 139 Rep(i, 4) totS += sc[i]; 140 } 141 142 bool operator< (const Player &x) const { 143 if(totS == x.totS) return name < x.name; 144 return totS < x.totS; 145 } 146 147 void print() { 148 if(isDQ) { 149 name.resize(31, ' '); 150 cout << name; 151 Rep(i, 4) { 152 if(sc[i] != 666666) printf("%-5d", sc[i]); 153 else printf(" "); 154 } 155 printf("DQ "); 156 return; 157 } 158 159 name += " " + toString(rank); 160 if(isTied && hasMoney) name.PB('T'); // 只有并列且拿钱才需要加 T 161 name.resize(31, ' '); 162 cout << name; 163 164 Rep(i, 4) printf("%-5d", sc[i]); 165 166 if(hasMoney) printf("%-10d$%9.2f ", totS, bonus / 100 + EPS); 167 else printf("%d ", totS); 168 } 169 }; 170 Player p[150]; 171 int num; 172 173 inline bool cmp(const Player &x, const Player &y) { 174 return x.sc[0] + x.sc[1] < y.sc[0] + y.sc[1]; 175 } 176 177 int T; 178 double totM, preRate[71]; 179 180 int main(){ 181 //freopen("MyOutput.txt","w",stdout); 182 //freopen("input.txt","r",stdin); 183 //INIT(); 184 scanf("%d ", &T); 185 while(T--) { 186 scanf("%lf ", &totM); 187 For(i, 1, 70) { 188 scanf("%lf ", &preRate[i]); 189 preRate[i] += preRate[i - 1]; 190 } 191 192 scanf("%d ", &num); 193 For(i, 1, num) { 194 string str; 195 getline(cin, str); 196 p[i] = Player(str); 197 } 198 sort(p + 1, p + 1 + num, cmp); 199 200 // 选前 70 名 201 For(i, 71, num) { 202 if(p[i].sc[0] + p[i].sc[1] != p[i - 1].sc[0] + p[i - 1].sc[1]) { 203 num = i - 1; 204 break; 205 } 206 } 207 208 sort(p + 1, p + 1 + num); 209 210 int i = 1, pcnt = 0; // pcnt 为已经给过奖金的人数 211 while(i <= num) { 212 if(p[i].isDQ) break; 213 int j = i - 1, cnt = 0; 214 215 while(j + 1 <= num && p[j + 1].totS == p[i].totS) if(!p[++j].isAmateur) ++cnt; 216 217 // [i, j] 为分数相同的区间,cnt记录这区间中非业余的人数 218 For(k, i, j) { 219 p[k].rank = i; 220 if(cnt >= 2 && !p[k].isAmateur) p[k].isTied = true; 221 222 if(cnt && pcnt < 70 && !p[k].isAmateur) { 223 p[k].hasMoney = true; 224 p[k].bonus = totM * (preRate[min(pcnt + cnt, 70)] - preRate[pcnt]) / cnt; 225 } 226 } 227 i = j + 1; 228 pcnt += cnt; 229 } 230 231 printf("Player Name Place RD1 RD2 RD3 RD4 TOTAL Money Won "); 232 printf("----------------------------------------------------------------------- "); 233 i = 1; 234 while(i <= num) p[i++].print(); 235 if(T) printf(" "); 236 } 237 return 0; 238 }