一.minimum scalar product
给你两个数组va和vb,两个数组的元素对应相乘,然后求和,要求和最小,在数组元素顺序可以调整的时候。这里主要用到一个不等式,x和y两个升序的数组,那么sum(x[i]*y[i])>=sum(x[i]*y[n-i])
void Fun(){
sort(v1,v1+n):
sort(v2,v2+n);
LL ans=0;
for(int i=0;i<n;++i)
ans+=(LL)v1[i]*v2[n-i-1];
printf("%lld\n",ans);
}
二.Crazy Rows
本题的题意是给你一个n*n的01矩阵,让你交换最小的相邻两行的次数使得该矩阵成为下三角矩阵。
思路很简单,我去记录每一行的最后一个1的位置a[i],然后该行如果移动,移动至第i行最优。
int a[MXN];//a[i]表示第i行最后出现的1的位置:1~n-1
void Fun(){
int res=0;
for(int i=0;i<N;++i){
a[i]=-1;
for(int j=0;j<N;++j)
if(M[i][j]==1) a[i]=j;
}
for(int i=0;i<N;++i){
int pos=-1;
for(int j=i;j<N;++j)
if(a[j]<=i){
pos=j;
break;
}
for(int j=pos;j>i;--j){
swap(a[j],a[j-1]);
res++;
}
}
printf("%d\n",res);
}
三.Bribe the Prisoners
题意是给你一排p个牢房,最初的时候每个牢房中有一个人,给出一个A数组表示我们要释放的人的位置,我们设定释放一个囚犯,我们要给他左边直到空牢房或墙和右边直到空牢房或墙的每个囚犯一枚金币。请你设计释放的顺序使得总花费最小,输出最小花费
int dp[MXQ][MXQ];//dp[i][j]表示将A[i]号囚犯到A[j]号囚犯的连续部分里所有囚犯都释放的话,最少金币数
void Fun(){
A[0]=0;
A[Q+1]=P+1;
for(int q=0;q<=Q;++q){
dp[q][q+1]=0;
}
for(int w=2;w<=Q+1;++w)
for(int i=0;i+w<=Q+1;++i){
int j=i+w,t=INF;
for(int k=i+1;k<j;++k){
t=min(t,dp[i][k]+dp[k][j]);
}
dp[i][j]=t+A[j]-A[i]-2;
}
printf("%d\n",dp[0][Q+1]);
}
四.Millionaire
题意是你要进行M轮的赌博,一开始你有初始金额X , 你每一轮获胜的概率都为P,当你最后持有的金钱 >= 1000000时算作取胜,给你M , P , X , 让你输出取胜的概率。
首先,由于赌注随意,可以为小数,所以无法直接暴枚,这里我们就会用到一个技巧“化连续为离散”。我们从最后一轮开始分类讨论,显然,最后一轮只有三种情况0 --- 500000 , 500000 --- 1000000 , 1000000 --- ;第一种获胜的概率为0,第二种为P,第三种为1。然后,我们再看倒数第二轮,分为5种状态:0 --- 250000 , 250000 --- 500000 , 500000 --- 750000 , 750000 --- 1000000 , 1000000 --- ;然后我们便得出第i轮的情况有2的i次幂 + 1种。然后枚举每种情况的期望,找到最优步骤即可
int M , X ;
double P;
double dp[2][(1 << 15) + 1];
void solve()
{
int n = 1 << M;
double *pre = dp[0] , *nxt = dp[1];
memset(pre , 0 , sizeof(double) * (n + 1));
/// memset(pre , 0 , sizeof(pre)); 这样初始化是不行的,因为pre为一个double型的指针,不是整个数组。
pre[n] = 1.0;
for(int r = 0 ; r < M; r++)///枚举第几轮
{
for(int i = 0 ; i <= n ; i++)///枚举当前是哪种状态
{
int step = min(i , n - i);///如果step大于n / 2 , 等会儿转移的时候可能会超过n
double t = 0.0;
for(int j = 0 ; j <= step ; j++)///枚举当前的所有可能走法
{
t = max(t , P * pre[i + j] + (1 - P) * pre[i - j]);///求出期望的最大值
}
nxt[i] = t;
}
swap(pre , nxt);///交换两个数组的值进行滚动
}
int i = (LL)X * n / 1000000;///找到X对应的是第几块
// for(int i = 0 ; i <= n ; i++)cout << '*' << pre[i] << endl;
printf("%.6lf\n" , pre[i]);
}