2424. 保龄球
题目链接
2424. 保龄球
JYY 很喜欢打保龄球,虽然技术不高,但是还是总想着的高分。
这里 JYY 将向你介绍他所参加的特殊保龄球比赛的规则,然后请你帮他得到尽量多的分数。
一场保龄球比赛一共有 \(N\) 个轮次,每一轮都会有 \(10\) 个木瓶放置在木板道的另一端。
每一轮中,选手都有两次投球的机会来尝试击倒全部的 \(10\) 个木瓶。
对于每一次投球机会,选手投球的得分等于这一次投球所击倒的木瓶数量。
选手每一轮的得分是他两次机会击倒全部木瓶的数量。
对于每一个轮次,有如下三种情况:
-
“全中”:如果选手第一次尝试就击倒了全部 \(10\) 个木瓶,那么这一轮就称为“全中”。在一个“全中”轮中,由于所有木瓶在第一次尝试中都已经被击倒,所以选手不需要再进行第二次投球尝试。同时,在计算总分时,选手在下一轮的得分将会被乘 \(2\) 计入总分。
-
“补中”:如果选手使用两次尝试击倒了 \(10\) 个木瓶,那么这一轮就称为“补中”。同时,在计算总分时,选手在下一轮中的第一次尝试的得分将会被乘以 \(2\) 计入总分。
-
“失误”:如果选手未能通过两次尝试击倒全部的木瓶,那么这一轮就被称为“失误”。同时,在计算总分时,选手在下一轮的得分会被计入总分,没有分数被翻倍。
此外,如果第 \(N\) 轮是“全中”,那么选手可以进行一次附加轮:也就是,如果第 \(N\) 轮是“全中”,那么选手将一共进行 \(N+1\) 轮比赛。
显然,在这种情况下,第 \(N+1\) 轮的分数一定会被加倍。
附加轮的规则只执行一次。
也就是说,即使第 \(N+1\) 轮选手又打出了“全中”,也不会进行第 \(N+2\) 轮比赛。
因而,附加轮的成绩不会使得其他轮的分数翻番。
最后,选手的总得分就是附加轮规则执行过,并且分数按上述规则加倍后的每一轮分数之和。
JYY 刚刚进行了一场 \(N\) 个轮次的保龄球比赛,但是,JYY 非常不满意他的得分。
JYY 想出了一个办法:他可以把记分表上,他所打出的所有轮次的顺序重新排列,这样重新排列之后,由于翻倍规则的存在,JYY 就可以得到更高的分数了!
当然了,JYY 不希望做的太假,他希望保证重新排列之后,所需要进行的轮数和重排前所进行的轮数是一致的:
比如如果重排前 JYY 在第 \(N\) 轮打出了“全中”,那么重排之后,第 \(N\) 轮还得是“全中”以保证比赛一共进行 \(N+1\) 轮;同样的,如果 JYY 第 \(N\) 轮没有打出“全中”,那么重排过后第 \(N\) 轮也不能是全中。
请你帮助 JYY 计算一下,他可以得到的最高的分数。
输入格式
第一行包含一个整数 \(N\),表示保龄球比赛所需要进行的轮数。
接下来包含 \(N\) 或者 \(N+1\) 行,第 \(i\) 行包含两个非负整数 \(X_i\) 和 \(Y_i\),表示 JYY 在这一轮两次投球尝试所得到的分数,\(X_i\) 表示第一次尝试,\(Y_i\) 表示第二次尝试。
我们用 10 0
表示一个“全中”轮。
输入数据保证合法,当且仅当 \(X_n = 10,Y_n = 0\) 时,存在 \(N+1\) 行 \(X_i\) 和 \(Y_i\)。
输出格式
输出一行一个整数,表示 JYY 最大可能得到的分数。
数据范围
\(1 \\le N \\le 50\)
输入样例:
2
5 2
10 0
3 7
输出样例:
44
样例解释
按照输入顺序,JYY 将得到 \(37\) 分。
最佳方案是将 \(3\) 个轮次排列成如下顺序:
3 7
10 0
5 2
解题思路
模拟退火
模拟退火可以用来解决连续函数的全局最优值问题,本题显然随机交换两个点变化不大,即是一个连续函数,故可用模拟退火来解决本问题
- 时间复杂度:\(O(随机)\)
代码
// Problem: 保龄球
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/2426/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=55;
PII a[N];
int n,m,res;
int cal()
{
int ret=0;
for(int i=0;i<m;i++)
{
ret+=a[i].fi+a[i].se;
if(i<n)
{
if(a[i].fi==10)ret+=a[i+1].fi+a[i+1].se;
else if(a[i].fi+a[i].se==10)ret+=a[i+1].fi;
}
}
res=max(res,ret);
return ret;
}
void simulate_anneal()
{
for(double t=1e4;t>1e-4;t*=0.99)
{
int x=rand()%m,y=rand()%m;
int xx=cal();
swap(a[x],a[y]);
if(n+(a[n-1].fi==10)==m)
{
int yy=cal();
if(exp((yy-xx)/t)<(double)rand()/RAND_MAX)swap(a[x],a[y]);
}
else
swap(a[x],a[y]);
}
}
int main()
{
cin>>n;
m=n;
for(int i=0;i<n;i++)cin>>a[i].fi>>a[i].se;
if(a[n-1].fi==10)
{
cin>>a[n].fi>>a[n].se;
m++;
}
while((double)clock()/CLOCKS_PER_SEC<0.8)simulate_anneal();
cout<<res<<'\n';
return 0;
}