3.22~3.23日刷题总结
Posted 卡卡卡卡罗特
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了3.22~3.23日刷题总结相关的知识,希望对你有一定的参考价值。
vj题组刷题
https://vjudge.net/contest/547627#problem/Q
思路:dfs+剪枝,剪枝掉没有必要的操作,降低时间复杂度,
确立r和h的遍历起点,r为递减,而r的大小不能小于0,所以r最小从0开始,h同理。
剪枝操作1:s+2*(N-v)/r>min_s这个时候可以直接返回不必继续搜索,当r取最大的时候,体积为最小,假如假设的最小体积比原来的min_s还要大,就一定不是最优解,没有必要继续搜索下去。
证明:
剪枝操作2:
情况1:if (min_v(m)>N-v)
情况2:if (max_v(r,h,m)<N-v)
这里的min_v的计算是从顶开始制作,以最小半径往下走,计算出的即为最小体积
这里的max_v的计算是从除了已经制作的蛋糕以外的最下面开始,半径为已经相邻层蛋糕的半径-1,此时为最大。
情况1:计算还没有制作的蛋糕的最小体积,假如这个体积大于剩余的体积,那么该情况不需要继续搜索
情况2:同理,计算还没有制作的蛋糕的最大体积,假如小于剩余的体积,也不需要继续搜索了
#include<iostream>
using namespace std;
int N,M;
int min_s=10e8;
int min_v(int m)//计算可以制作的最大体积
int sum=0;
for (int i=M-m;i>=1;i--)//从最上层开始,以达到体积最小
sum+=i*i*i;
return sum;
int max_v(int r,int h,int m)//计算可以制作蛋糕的最大体积
int sum=0;
for (int i=r,j=h;m<=M;i--,j--)//从制作的上一层开始,继续制作以达到最大
sum+=i*i*j;
m++;
return sum;
void dfs(int r,int h,int s,int v,int m)
if(v==N&&m==M&&s<min_s)
min_s=s;//更新答案
return ;
if(r==0||s+2*(N-v)/r>min_s)return ;//无必要的搜索剪枝操作1
if (min_v(m)>N-v)return;//无必要的搜索//剪枝操作2
if (max_v(r,h,m)<N-v)return;//无必要的搜索
for (int i=r-1;i>=M-m;i--)//枚举上一层半径
for (int j=h-1;j>=M-m;j--)//枚举上一层的高
dfs(i,j,s+2*i*j,v+i*i*j,m+1);//搜索上一层
int main()
cin>>N>>M;
for(int i=M;i*i<=N;i++)//枚举半径
for(int j=N/(i*i);j>=M;j--)//枚举高度
int s=i*i+2*i*j;
int v=i*i*j;
dfs(i,j,s,v,1);//第一层
if(min_s==10e8)
cout<<0<<endl;
else cout<<min_s<<endl;
return 0;
https://vjudge.net/contest/547627#problem/P
思路:dfs+回溯,回溯出满足题意的所有回答问题的个数,搜的过程中不断更新结果得到这些结果中的最大值即可。
#include<iostream>
#include<cstring>
#include<algorithm>
#define N 16
using namespace std;
int n,ans;
int book[N];
int a[16][16];
void dfs(int s,int sum,int cost)
ans=max(ans,sum);//更新答案
if(sum==n)return ;//回答了n个问题
for(int i=1;i<n;i++)
if(book[i]==0&&a[s][i]>=cost)//这个题没做过,而且这个题的难度大于等于上一个题的难度
book[i]=1;//标记为做过
dfs(i,sum+1,a[s][i]);
book[i]=0;//回溯
int main()
while(cin>>n)
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
cin>>a[i][j];
ans=0;//初始化
memset(book,0,sizeof(book));//初始化
dfs(0,1,0);
book[0]=1;
cout<<ans<<endl;
https://vjudge.net/contest/547627#problem/L
思路:这个题困扰了我太久太久,在纸上模拟了好久循环过程中pos数组的变化,才体会到其中的奥秘,答案不是唯一的,因此设立一个数组,作为模板,与模板进行对比,利用pos数组存储各个序列已经用掉的元素个数。
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 10
using namespace std;
int deep,ans;
char a[N][N];
int n;
char DNA[4]='A','G','C','T';
void dfs(int s,int len[])
if(s>deep) return;//大于限制的深度,不用往下搜索
int max_=0;
for(int i=0;i<n;i++)//求预计长度,预计长度为最好的情况下的长度
int k=strlen(a[i])-len[i];
max_=max(k,max_);//其中一个最大的即为最理想情况下的长度(满足最长的即可)
if(max_==0)//预计长度为0,说明匹配完成
ans=s;
return;
if(s+max_>deep)return;//已经构造了的长度+预计长度若大于设置的长度,就返回
for(int i=0;i<4;i++)//搜DNA数组中的四个元素
int flag=0;
int pos[10];
for(int j=0;j<n;j++)//n个序列的遍历
if(a[j][len[j]]==DNA[i])//和目标相同
flag=1;
pos[j]=len[j]+1;//这一行序列 列+1
else
pos[j]=len[j];//不相等,列不动
if(flag==1)//出现了相等的情况就继续往下一列搜,如果不相等,就遍历DNA数组的另一个元素,因为要先保证连续性
dfs(s+1,pos);
if(ans!=-1) return;
int main()
int t;
cin>>t;
while(t--)
cin>>n;
deep=0;
for(int i=0;i<n;i++)
cin>>a[i];
int k=strlen(a[i]);
deep=max(deep,k);
ans=-1;
int pos[10]=0;
while(1)
dfs(0,pos);
if(ans!=-1)//得到结果了
cout<<ans<<endl;break;
deep++;
return 0;
cf补题情况:
https://codeforces.com/contest/1807/problem/G1
思路:构造目标数列中元素的顺序,不影响数列的最终结果,因为构造出的元素可以插到任意位置,因此我们先对其进行排序,依次构造过去,这里需要特别判断一下目标数组的数组头,一定要为1,否则无法构造出来,其次判断前缀和和下一个元素的大小,如果这个元素大于这个元素前的前缀和,那么便无法构造。
#include<iostream>
#include<algorithm>
using namespace std;
int a[200005];
int main()
int T;
cin>>T;
while(T--)
int n;
cin>>n;
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
int temp=1;
bool flag=true;
sort(a,a+n);
for(int i=1;i<n;i++)
if(temp>=a[i])
temp+=a[i];//计算前缀和
else //前缀和小于这个元素,无法构造
flag=false;break;
if(a[0]==1)//特别判断数组首元素
if(flag==true)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
else
cout<<"NO"<<endl;
https://codeforces.com/contest/1807/problem/G2
思路:hard版数据更大,我们需要开 long long ,时间上的话,hard版的时间刚好在10e9的位置左右,改用scanf输入加快速度,循环遍历搜索到不能构造的情况直接break,减少时间,代码变化不大,可以ac。
#include<iostream>
#include<algorithm>
#define ull unsigned long long
using namespace std;
int T;
bool flag=true;
ull a[200005];
void check()
if(a[0]==1)
if(flag==true)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
else
cout<<"NO"<<endl;
void do_()
while(T--)
int n;
cin>>n;
for(int i=0;i<n;i++)
scanf("%llu",&a[i]);
ull temp=1;
sort(a,a+n);
flag=true;
for(int i=1;i<n;i++)
if(temp>=a[i])
temp+=a[i];
else
flag=false;
break;//不能构造去跳出循环
check();
int main()
scanf("%d",&T);
do_();
java学习情况:
笔记详情:
总结:vj题组加油刷题,cf补题还差一个。
4月9日刷题笔记
Collection 是对象集合, Collection 有两个子接口 List 和 Set,
List 可以通过下标 (1,2..) 来取得值,值可以重复,而 Set 只能通过游标来取值,并且值是不能重复的
ArrayList , Vector , LinkedList 是 List 的实现类
ArrayList 是线程不安全的, Vector 是线程安全的,这两个类底层都是由数组实现的
LinkedList 是线程不安全的,底层是由链表实现的
Map 是键值对集合
HashTable 和 HashMap 是 Map 的实现类
HashTable 是线程安全的,不能存储 null 值
HashMap 不是线程安全的,可以存储 null 值
对于这道题,考察的是对String类型的认识以及编译器优化。Java中String不是基本类型,但是有些时候和基本类型差不多,如String b = "tao" ; 可以对变量直接赋值,而不用 new 一个对象(当然也可以用 new)。所以String这个类型值得好好研究下。
Java中的变量和基本类型的值存放于栈内存,而new出来的对象本身存放于堆内存,指向对象的引用还是存放在栈内存。例如如下的代码:
int i=1;
String s = new String( "Hello World" );
变量i和s以及1存放在栈内存,而s指向的对象”Hello World”存放于堆内存。
栈内存的一个特点是数据共享,这样设计是为了减小内存消耗,前面定义了i=1,i和1都在栈内存内,如果再定义一个j=1,此时将j放入栈内存,然后查找栈内存中是否有1,如果有则j指向1。如果再给j赋值2,则在栈内存中查找是否有2,如果没有就在栈内存中放一个2,然后j指向2。也就是如果常量在栈内存中,就将变量指向该常量,如果没有就在该栈内存增加一个该常量,并将变量指向该常量。
如果j++,这时指向的变量并不会改变,而是在栈内寻找新的常量(比原来的常量大1),如果栈内存有则指向它,如果没有就在栈内存中加入此常量并将j指向它。这种基本类型之间比较大小和我们逻辑上判断大小是一致的。如定义i和j是都赋值1,则i==j结果为true。==用于判断两个变量指向的地址是否一样。i==j就是判断i指向的1和j指向的1是同一个吗?当然是了。对于直接赋值的字符串常量(如String s=“Hello World”;中的Hello World)也是存放在栈内存中,而new出来的字符串对象(即String对象)是存放在堆内存中。如果定义String s=“Hello World”和String w=“Hello World”,s==w吗?肯定是true,因为他们指向的是同一个Hello World。
堆内存没有数据共享的特点,前面定义的String s = new String( "Hello World" );后,变量s在栈内存内,Hello World 这个String对象在堆内存内。如果定义String w = new String( "Hello World" );,则会在堆内存创建一个新的String对象,变量w存放在栈内存,w指向这个新的String对象。堆内存中不同对象(指同一类型的不同对象)的比较如果用==则结果肯定都是false,比如s==w?当然不等,s和w指向堆内存中不同的String对象。如果判断两个String对象相等呢?用equals方法。
说了这么多只是说了这道题的铺垫知识,还没进入主题,下面分析这道题。 MESSAGE 成员变量及其指向的字符串常量肯定都是在栈内存里的,变量 a 运算完也是指向一个字符串“ taobao ”啊?是不是同一个呢?这涉及到编译器优化问题。对于字符串常量的相加,在编译时直接将字符串合并,而不是等到运行时再合并。也就是说
String a = "tao" + "bao" ;和String a = "taobao" ;编译出的字节码是一样的。所以等到运行时,根据上面说的栈内存是数据共享原则,a和MESSAGE指向的是同一个字符串。而对于后面的(b+c)又是什么情况呢?b+c只能等到运行时才能判定是什么字符串,编译器不会优化,想想这也是有道理的,编译器怕你对b的值改变,所以编译器不会优化。运行时b+c计算出来的"taobao"和栈内存里已经有的"taobao"是一个吗?不是。b+c计算出来的"taobao"应该是放在堆内存中的String对象。这可以通过System. out .println( (b+c)== MESSAGE );的结果为false来证明这一点。如果计算出来的b+c也是在栈内存,那结果应该是true。Java对String的相加是通过StringBuffer实现的,先构造一个StringBuffer里面存放”tao”,然后调用append()方法追加”bao”,然后将值为”taobao”的StringBuffer转化成String对象。StringBuffer对象在堆内存中,那转换成的String对象理所应当的也是在堆内存中。下面改造一下这个语句System. out .println( (b+c).intern()== MESSAGE );结果是true, intern() 方法会先检查 String 池 ( 或者说成栈内存 ) 中是否存在相同的字符串常量,如果有就返回。所以 intern()返回的就是MESSAGE指向的"taobao"。再把变量b和c的定义改一下,
final String b = "tao" ;
final String c = "bao" ;
System. out .println( (b+c)== MESSAGE );
现在b和c不可能再次赋值了,所以编译器将b+c编译成了”taobao”。因此,这时的结果是true。
在字符串相加中,只要有一个是非final类型的变量,编译器就不会优化,因为这样的变量可能发生改变,所以编译器不可能将这样的变量替换成常量。例如将变量b的final去掉,结果又变成了false。这也就意味着会用到StringBuffer对象,计算的结果在堆内存中。
如果对指向堆内存中的对象的String变量调用intern()会怎么样呢?实际上这个问题已经说过了,(b+c).intern(),b+c的结果就是在堆内存中。对于指向栈内存中字符串常量的变量调用intern()返回的还是它自己,没有多大意义。它会根据堆内存中对象的值,去查找String池中是否有相同的字符串,如果有就将变量指向这个string池中的变量。
String a = "tao"+"bao";
String b = new String("taobao");
System.out.println(a==MESSAGE); //true
System.out.println(b==MESSAGE); //false
b = b.intern();
System.out.println(b==MESSAGE); //true
System. out .println(a==a.intern()); //true
以上是关于3.22~3.23日刷题总结的主要内容,如果未能解决你的问题,请参考以下文章