A[傻逼题]
大意:给你a,b,c三个数,你可以在其中加上括号,加号,乘号,使得到的值最大
就是问你 a+b+c,a*(b+c),(a+b)*c,a*b*c,(a+c)*b 哪个最大!
我去...这不是神级傻逼题么...
然后我特别认真的想了一下觉得貌似可以偷懒,按从小到大排序一下,比较 a*b*c,(a+b)*c 这两个的值就可以了!!
卧槽....还有 a+b+c这种情况啊!就比如1 1 1
妈蛋....整个人都不好了...就因为我的傻逼wa了两次!!
B[贪心+排序]
大意:你有n个塔,每个塔都有自己的初始高度ai(ai层),你可以进行k次操作,把其中的一个塔的1层移到另一个塔上,问你最高的塔和最低的塔的差值最小为多少?要操作几次才得到这个最小值,并打印出任意一个可行的方案;
排序一遍,得到最大高度和最小高度;
访问k次操作:
如果最大高度-最小高度>=2,就移动;ans++;(真正操作的次数),记录下移动的号数;
如果次大比刚开始的最大高度大,或是次小的比刚开始最小的高度小,就再排序一遍;
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
int n,k;
struct node{
int x,num;
}a[101];
int b[3][1001];
bool cmp(const node &x,const node &y){
return x.x>y.x?1:0;
}
int main(){
//freopen("data.txt","r",stdin);
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&a[i].x);
a[i].num=i;
}
sort(a+1,a+n+1,cmp);
int temp;
int ans=0;
for(int i=1;i<=k;i++){
temp=a[1].x-a[n].x;
if(temp>=2){
b[1][++ans]=a[1].num;
a[1].x--;
b[2][ans]=a[n].num;
a[n].x++;
temp-=2;
}
if(a[1].x<a[2].x || a[n-1].x<a[n].x){
sort(a+1,a+n+1,cmp);
}
temp=a[1].x-a[n].x;
}
printf("%d %d\n",temp,ans);
for(int i=1;i<=ans;i++){
printf("%d %d\n",b[1][i],b[2][i]);
}
return 0;
}
C[贪心+排序]
为何感觉跟上题的思想差不多,甚至写起来更简单。不附上代码了。
D[思考题?+map]
大意:有n个ai,其中a[1]=0,a[n]=L;问你a数组中是否存在相差为x,和相差为y的数;
如果没有,你需要添加元素,满足以上条件;
给你n,L,x,y;让你求出要添加几个元素,添加的元素是什么(任意输出一种情况即可)
不懂把D题归为哪里,所以加上了问号。
我觉得这题应该是这一套中最棒的一题吧,为什么说棒呢?不像E题,那纯考自己DP的能力。这一题,考的更多的应该是思维方面吧;
当然也有可能是我平时没有写过或是没有想过这样的题的缘故,看完这题的范围,觉得无论怎么写应该都会超时吧或者是用我没有学过的区间上的算法之类的...受大神点拨之后,顿时有一种整个人都升华了的感觉!
首先,如果把问题简单的化为,如果只是判断是否存在差值为x的两个数和差值为y的两个数,应该怎么做?
暴力两遍for?...n<=10^5..这样做怎么可能!
为什么不换一个思路,看看a数组中是否存在 a[i]+x或a[i]+y 的元素呢?
只要想到了这种方法,存入数据的时候,加上hash就能轻松解决了!
#include <map>
map<int , int >b;
b[a[i]]=1;
之后一遍for,看看是否存在,不就解决问题了么?
接下来,我们想如果都存在,那添加的个数为0;
如果初次判断a中都不存在,那就要分两种情况判断是要添加一个数还是两个数
因为会出现这样的情况,例如 有a,b,c三个数,初次判断并不存在某两数相差为x或为y,但是这时候是否需要添加两个数呢?答案是不一定的,会不会存在情况,添加的数d,与a的差为x,与b的差值为y呢?
这是我们需要判断的。如何判断?
好像很复杂?其实不然。试想如果真的存在这种情况,设 d+x 或 d-x 的值为 z, 则z+y 或 z-y 必定会在已知数组中;
所以,只要讨论 d-x+y , d+x+y(和d-x-y情况相同,可省略一个) , d+x-y 是否在数组中即可!
注意一下 d+x<=l, d-x>=0;
如果,还是都不存在,就添加 x,y,因为有a[1]=0;
附上代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <map>
using namespace std;
int a[100005];
long long l,x,y;
map<int,int>f;
int n;
bool temp1,temp2;
int main(){
//freopen("data.txt","r",stdin);
cin>>n>>l>>x>>y;
for(int i=1;i<=n;i++){
cin>>a[i];
f[a[i]]=1;
}
for(int i=1;i<=n;i++){
if(f[a[i]-x]) temp1=true;
if(f[a[i]-y]) temp2=true;
}
if(temp1 && temp2){
cout<<"0";
return 0;
}
if(temp1){
cout<<"1\n"<<y;
return 0;
}
if(temp2){
cout<<"1\n"<<x;
return 0;
}
if(!temp1 && !temp2){
for(int i=1;i<=n;i++){
if(f[a[i]-x-y]){
cout<<"1\n"<<a[i]-x;
return 0;
}
if(f[a[i]-x+y] && a[i]-x>=0){
cout<<"1\n"<<a[i]-x;
return 0;
}
if(f[a[i]+x-y] && a[i]+x<=l){
cout<<"1\n"<<a[i]+x;
return 0;
}
}
}
cout<<"2\n"<<x<<" "<<y;
return 0;
}
E[DP]
大意:有n层楼,有一个人最初在第a层,他想k次电梯玩,但是第b层不能去;
当你此时在x层, 想要坐到y层时要满足 |x-y|<|x-b|,问你可行的方案数mod1000000007是多少
很明显的DP题,但是普通dp有三重循环,会超时。
怎么优化?
我们试想,如果某一层满足以上要求,那么是否存在有一段区间也都是满足以上要求的呢?
当然存在了,但是.....怎么表示?
有点像前缀和?没错!就是用前缀和优化DP;
f[i][j]表示,做了i次电梯,到达j层的方案数;
sum[j]表示,1-j层,方案数总数;
状态转移方程就为:
f[0][a]=1;
for(int i=1;i<=k;i++){
for(int j=1;j<=n;j++) sum[j]=sum[j-1]+f[i-1][j];
for(int j=1;j<=n;j++){
if(j>b){
f[i][j]=(sum[n]-sum[j-(j-b-1)/2-1]-f[i-1][j])%m;
}else{
f[i][j]=(sum[j+(b-j-1)/2]-f[i-1][j])%m;
}
f[i][b]=0;
}
}
把那个不等式拆开,讨论j>b和j<=b的情况即可;
j>b的时候,n~ j-(j-b-1)/2 -1 都是可行的,为什么这里减去 f[i-1][j]呢?
因为,如果上一层已经为j了,你再做了一次电梯怎么可能还为j?!当然要减去自己的这层的方案数了;
最后 ans=(ans+f[k][i])%modn;
sum[i]只是个前缀和,f[k][i]才是各个层的方案数!
注意sum,和ans 都需要 long long
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int m=1000000007;
int n,a,b,k;
long long maxn;
long long sum[5001];
int f[5001][5001];
int main(){
scanf("%d%d%d%d",&n,&a,&b,&k);
f[0][a]=1;
for(int i=1;i<=k;i++){
for(int j=1;j<=n;j++) sum[j]=sum[j-1]+f[i-1][j];
for(int j=1;j<=n;j++){
if(j>b){
f[i][j]=(sum[n]-sum[j-(j-b-1)/2-1]-f[i-1][j])%m;
}else{
f[i][j]=(sum[j+(b-j-1)/2]-f[i-1][j])%m;
}
f[i][b]=0;
}
}
for(int i=1;i<=n;i++) maxn=(maxn+f[k][i])%m;
cout<<maxn;
return 0;
}
BLESS ALL