前言
最近考试考得挺多的……这次算,考得还行?$\text{rk16}$。
话说 Peter 崛起了啊,$\text{rk7}$……
然鹅我和他都因为数组开小少得了 $\text{60pts}$。以后别犯这种低级错误了啊……
OJ 最终成绩:$40+100+55+5+0=200$
Lemon 上不知道。
快乐的题解时间
题意简述:
有 $T$ 组数据,每组数据给定 $n$ 个数字,记为 $a_1,a_2,\ldots a_n$。在这一组数据中选出 $5$ 个数字使得这 $5$ 个数字的乘积最大。
果然是不开 long long
见祖宗啊……
还有就是数组开小,后 $10$ 个点全 RE 了……
我直接 AC $\text{100pts}→$ WA + RE $\text{40pts}$……
法一:贪心
我们知道乘积要最大,一定要让它尽量为正数。
那么,首先我们先按照绝对值的大小将数组从大到小排序。然后我们看一看:
如果前 $5$ 个数字中有 $0$,那说明这组数据中不为 $0$ 的数少于 $5$ 个。那么结果一定为 $0$。
如果前 $5$ 个数字的乘积为正数,那么这个乘积一定是最优解。证明很简单,相信大家都明白。
那么如果这 $5$ 个数的乘积是负数呢?
那么首先我们分析最小那个数(即第 $5$ 个数)是否可以被换成一个和它正负相反的数字。因为我们知道,当一个乘积为负的时候,只需要换一个就可以了。找当然是从前往后找,找到一个就立刻输出,然后 return
。因为这已经是最优解了。
继续,如果找不到怎么办?那我们就找与 $a_4,a_3,a_2,a_1$ 正负相反的数来代替它(此时的 $a$ 数组已经排过序,还是按照绝对值从大往小找保证找到的是最优解,当然,前 $4$ 个数字要从小往大找)。
那如果还是找不到呢?那么,这组数据一定是不能凑出正数。那么我们就只能找绝对值最小的 $5$ 个数(包括 $0$)来凑乘积了。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| #include<bits/stdc++.h> #define int long long using namespace std; int t,n,a[100005]; bool f; bool cmp(int x,int y){return abs(x)>abs(y);} bool cmp2(int x,int y){return abs(x)<abs(y);} void s(){ int h=0; bool b[15]={}; for(int i=1;i<=5;i++){ b[i]=(a[i]<0); h+=(int)b[i]; if(!a[i]){ printf("0\n"); return; } } if(!(h&1)){ printf("%lld\n",a[1]*a[2]*a[3]*a[4]*a[5]); return; } bool d=!b[5]; for(int i=6;i<=n;i++){ if((a[i]<0)==d){ printf("%lld\n",a[1]*a[2]*a[3]*a[4]*a[i]); return; } } for(int i=4;i;i--){ if(b[i]!=b[5]){ int y=a[1]*a[2]*a[3]*a[4]*a[5]; y/=a[i]; for(int j=6;j<=n;j++){ if((a[j]<0)!=b[i]){ y*=a[j]; printf("%lld\n",y); return; } } } } sort(a+1,a+n+1,cmp2); printf("%lld\n",a[1]*a[2]*a[3]*a[4]*a[5]); return; } signed main(){ freopen("maximum.in","r",stdin); freopen("maximum.out","w",stdout); scanf("%lld",&t); while(t--){ f=0; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lld",&a[i]),f=f|(!a[i]); sort(a+1,a+n+1,cmp); s(); } return 0; }
|
法二:DP
As we all know,最大的乘积是有可能从最小的数(绝对值比较大)乘一个负数得到~
所以,如果我们使用 DP 来求解这个问题的话,需要开两个 $dp$ 数组来分别存最大值和最小值。
如果要求最大值(至少得是个正数吧),有两种方法来求:
两个正数(其中一个是前面的最大值)相乘;
两个负数(其中一个是前面的最小值)相乘。
求最小值也有两种方法,不过我现在用的这个电脑输入法实在是太不好用了,所以我真的不想写了。。。
见代码……
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #include<bits/stdc++.h> #define ll long long using namespace std; int t,n,a[100005]; ll dp1[100005][6],dp2[100005][6]; signed main(){ freopen("maximum.in","r",stdin); freopen("maximum.out","w",stdout); scanf("%d",&t); while(t--){ memset(dp1,128,sizeof(dp1)); memset(dp2,127,sizeof(dp2)); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=0;i<=n;i++) dp1[i][0]=dp2[i][0]=1; for(int i=1;i<=n;i++){ for(int j=1;j<=min(i,(int)5);j++){ dp1[i][j]=max(max(dp1[i-1][j-1]*a[i],dp2[i-1][j-1]*a[i]),dp1[i-1][j]); dp2[i][j]=min(min(dp1[i-1][j-1]*a[i],dp2[i-1][j-1]*a[i]),dp2[i-1][j]); } } printf("%lld\n",dp1[n][5]); } return 0; }
|
水题,打表即可……
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| #include<bits/stdc++.h> using namespace std; int I,V,X,L,C,D,M,n; string sc(int x){ int qian=x/1000; x%=1000; int bai=x/100; x%=100; int shi=x/10; x%=10; string s; if(qian==1) s="M"; else if(qian==2) s="MM"; else if(qian==3) s="MMM"; if(bai==1) s+="C"; else if(bai==2) s+="CC"; else if(bai==3) s+="CCC"; else if(bai==4) s+="CD"; else if(bai==5) s+="D"; else if(bai==6) s+="DC"; else if(bai==7) s+="DCC"; else if(bai==8) s+="DCCC"; else if(bai==9) s+="CM"; if(shi==1) s+="X"; else if(shi==2) s+="XX"; else if(shi==3) s+="XXX"; else if(shi==4) s+="XL"; else if(shi==5) s+="L"; else if(shi==6) s+="LX"; else if(shi==7) s+="LXX"; else if(shi==8) s+="LXXX"; else if(shi==9) s+="XC"; if(x==1) s+="I"; else if(x==2) s+="II"; else if(x==3) s+="III"; else if(x==4) s+="IV"; else if(x==5) s+="V"; else if(x==6) s+="VI"; else if(x==7) s+="VII"; else if(x==8) s+="VIII"; else if(x==9) s+="IX"; return s; } int main(){ freopen("introduction.in","r",stdin); freopen("introduction.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++){ string s=sc(i); int j=0; while(s[j]){ if(s[j]=='I') I++; else if(s[j]=='V') V++; else if(s[j]=='X') X++; else if(s[j]=='L') L++; else if(s[j]=='C') C++; else if(s[j]=='D') D++; else if(s[j]=='M') M++; j++; } } if(I) printf("I %d\n",I); if(V) printf("V %d\n",V); if(X) printf("X %d\n",X); if(L) printf("L %d\n",L); if(C) printf("C %d\n",C); if(D) printf("D %d\n",D); if(M) printf("M %d\n",M); return 0; }
|
和 LIS 问题的普通版求解差不多,只是需要多开一维记录前一个是上升还是下降。
zmq 说也可以用贪心统计折点的数量再 $+1$,但是我交了之后只有 $\text{45pts}$,也不知道是我实现错了还是他说错了。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include<bits/stdc++.h> using namespace std; int n,num,dp[10005][2],a[10005]; bool b[10005]; int main(){ freopen("sawtooth.in","r",stdin); freopen("sawtooth.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); dp[1][0]=dp[1][1]=1; for(int i=2;i<=n;i++){ for(int j=1;j<i;j++){ if(a[i]<a[j]) dp[i][0]=max(dp[i][0],dp[j][1]+1); if(a[i]>a[j]) dp[i][1]=max(dp[i][1],dp[j][0]+1); } num=max(num,max(dp[i][0],dp[i][1])); } printf("%d",num); return 0; }
|
$dp_{i,j}$ 表示前 $i$ 只 mjl 马进前 $j$ 个马棚产生的最小不愉快系数。
对于每一只 mjl 马,我们可以把它和它前面的 一些 mjl 马一起装在一个马棚里,说具体点,就是再用一层循环,$q$ 代表从第 $q$ 只 mjl 马开始就和第 $i$ 只 mjl 马装在一个马棚里。这样产生的不愉快系数就是 $dp_{q-1,j-1}$ + 这个马棚里的不愉快系数。那这个不愉快系数怎么算呢?用前缀和统计一下这个区间里黑 mjl 马和白 mjl 马的数量再相乘就可以了。具体看代码。
$dp$ 初始化成 $\text{INF}$,$dp_{0,0}=0$。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include<bits/stdc++.h> using namespace std; int n,k,a[505],b[505],w[505],dp[505][505]; int main(){ freopen("horse.in","r",stdin); freopen("horse.out","w",stdout); memset(dp,127,sizeof(dp)); dp[0][0]=0; scanf("%d %d",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++){ w[i]=w[i-1]+(!a[i]); b[i]=b[i-1]+a[i]; } for(int i=1;i<=n;i++){ for(int j=1;j<=min(i,k);j++){ for(int q=j;q<=i;q++){ dp[i][j]=min(dp[i][j],dp[q-1][j-1]+(w[i]-w[q-1])*(b[i]-b[q-1])); } } } printf("%d",dp[n][k]); return 0; }
|
$50$ 分就是一个分组背包。
$100$ 分好像是要开专门记录有或者没有奖励子弹的情况。
没太听明白,放个代码留在这吧。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| #include<bits/stdc++.h> using namespace std; int m,n,k; int f[205][205]; bool h[205][205]; char a; int dy[205][205],dn[205][205],dpy[205][205],dpn[205][205]; int main(){ scanf("%d %d %d",&m,&n,&k); for(int i=1;i<=m;i++){ for(int j=1;j<=n;j++){ scanf("%d %c",&f[i][j],&a); if(a=='Y') h[i][j]=1; } } for(int i=1;i<=n;i++){ int tot=0; for(int l=m;l;l--){ if(h[l][i]){ dy[i][tot]+=f[l][i]; }else{ tot++; dn[i][tot]=dy[i][tot-1]+f[l][i]; dy[i][tot]=dn[i][tot]; } } } for(int i=1;i<=n;i++){ for(int j=0;j<=k;j++){ for(int q=0;q<=min(k,j);q++){ dpy[i][j]=max(dpy[i][j],dpy[i-1][j-q]+dy[i][q]); if(q) dpn[i][j]=max(dpn[i][j],dpy[i-1][j-q]+dn[i][q]); if(j-q) dpn[i][j]=max(dpn[i][j],dpn[i-1][j-q]+dy[i][q]); } } } printf("%d",dpn[n][k]); return 0; }
|