1、补Codeforces Round #552 (Div. 3) E、F、G题
2、补完Codeforces Round #582 (Div. 3)
题目链接:https://codeforces.com/contest/1154/problem/E
AC代码,没有掌握模拟链表的精髓,比较暴力
#include
using namespace std;
int n,k;
struct pp{
int x;
int inx;
bool operator<(pp b){
return x>b.x;
}
}a[200007];
int ans[200007],L[200007],R[200007];
int main(){
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++){
scanf("%d",&a[i].x);
a[i].inx=i;
L[i]=i-1;
R[i]=i+1;
}
sort(a,a+n);
int now=1;
for(int i=0;i<n;i++){
int inx=a[i].inx;
if(ans[inx]) continue;
int cnt=0;
int l=L[inx];
int r=R[inx];
ans[inx]=now;
while(l>=0&&cnt<k){
while(l>=0&&ans[l]){//找到满足条件的l
l=L[l];
}
if(l>=0){
ans[l]=now;
// l=L[l];
cnt++;
}
}
cnt=0;
while(r<n&&cnt<k){
while(r<n&&ans[r]){//找到满足条件的l
r=R[r];
}
if(r<n){
ans[r]=now;
// r=R[r];
cnt++;
}
}
L[inx]=l;//乱写ing,只是在改变了已经从链表中删除的值,毫无卵用,导致每次去找满足条件的r和l时,还要用一个while循环,复杂度上去了,而且写得繁琐,没有实现用链表的好处。
R[inx]=r;
if(now==1) now=2;
else now=1;
}
for(int i=0;i<n;i++){
// if(i) printf(" ");
printf("%d",ans[i]);
}
printf("\n");
}
改进后的代码:
#include
using namespace std;
int n,k;
struct pp{
int x;
int inx;
bool operator<(pp b){
return x>b.x;
}
}a[200007];
int ans[200007],L[200007],R[200007];
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){//因为数组有用到下标操作,所以从1开始更容易写
scanf("%d",&a[i].x);
a[i].inx=i;
L[i]=i-1;
R[i]=i+1;
}
sort(a+1,a+n+1);
int now=1;
for(int i=1;i<=n;i++){
int inx=a[i].inx;
if(ans[inx]) continue;
int cnt=0;
int l=L[inx];
int r=R[inx];
ans[inx]=now;
while(l>0&&cnt<k){
ans[l]=now;
l=L[l];
cnt++;
}
cnt=0;
while(r<=n&&cnt<k){
ans[r]=now;
r=R[r];
cnt++;
}
R[l]=r;//改变没有删除的数的左右边界
L[r]=l;//通过这样的操作,就可以形成一个新的链表,使得剩余的数又连了起来
if(now==1) now=2;
else now=1;
}
for(int i=1;i<=n;i++){
printf("%d",ans[i]);
}
printf("\n");
}
心得反思:如果题目中主要运用到增、删操作可以考虑用链表实现,因为链表实现的话复杂度最低,这道题还可以用线段树做,也可以转换成线段树区间覆盖问题。
思路:我们把选出来k件最便宜的商品中,从小到大排个序,此时满减最优惠的就是在相邻的商品中使用,因为它每次只能减掉最便宜的,此时这样的话就最优。
#include
using namespace std;
typedef long long ll;
int n,k,m,x,y;
ll sum[200007],a[200007];
int c[200007];
ll dp[200007];
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int i=0;i<m;i++){
scanf("%d%d",&x,&y);
c[x]=max(c[x],y);//用数组c来记录满x件时,可以减免的最大数量
}
sort(a+1,a+n+1);
for(int i=1;i<=k;i++){
sum[i]=sum[i-1]+a[i];
// printf("%d : %lld\n",i,sum[i]);
}
memset(dp,0x3f3f3f3f,sizeof(dp));
dp[0]=0;
for(int i=1;i<=k;i++){
dp[i]=min(dp[i],dp[i-1]+a[i]);
for(int j=0;j<=i;j++){
dp[i]=min(dp[i],dp[j]+sum[i]-sum[j]-(sum[j+c[i-j]]-sum[j]));
//在连续子序列 [j+1,i]中,使用满减活动和不使用取最小花费
//利用前缀和来实现每段的求和,sum[i]-sum[j]即区间【j+1,i】的总花费
//可以减掉c[i-j]数量的价格,即需要减掉区间【j+1,j+c[i-1]】的总花费
}
}
printf("%lld\n",dp[k]);
}
思路:
LCM(a,b) = a * b / gcd(a,b);若x1、x2、x3、x4…都含有最大公因子d,且x1
当d是x1、x2的最大公因子则:LCM(x1,x2)=x1x2/d 若d不是x1、x2的最大公因子则:LCM(x1,x2) x2/d 则我们就可以枚举公因子i,取前两项就可以求得整个序列的最小LCM的值。
取前两个肯定是当前因子最小的情况,枚举完每个因子,就能得到整体最小的LCM的值。
如果找不到两个数拥有一个相同的最大公因子,则ans就是最小的两个数的乘积了,此时他们的最大公因子是1。
#include
using namespace std;
typedef long long ll;
int n;
const int maxn=1e7+7;
struct pp{
int x;
int inx;
bool operator<(pp b){
return x<b.x;
}
}a[maxn];
int cnt[maxn],Inx[maxn];
ll lcm(ll a,ll b){
return a/__gcd(a,b)*b;
}
int main(){
// printf("%lld\n",lcm(10000000,9999000));
scanf("%d",&n);
int inx1=-1;
int inx2=-1;
ll Min=1ll*maxn*maxn;//int*int还是int,要强转为long long,要不是看到错误样例,我死也不会知道是这里错了
for(int i=0;i<n;i++){
scanf("%d",&a[i].x);
a[i].inx=i;
cnt[a[i].x]++;
if(cnt[a[i].x]>=2&&a[i].x<Min){
Min=a[i].x;
inx1=i;
inx2=Inx[a[i].x];
}
Inx[a[i].x]=i;
}
ll Lcm,A,B;
for(int i=1;i<maxn;i++){
int cc=0;
for(int j=i;j<maxn;j+=i){
if(!cnt[j]) continue;
if(cc==0){
A=j;
cc++;
}
else if(cc==1){
B=j;
Lcm=lcm(A,B);
if(Lcm<Min){
Min=Lcm;
inx1=Inx[A];
inx2=Inx[B];
// printf("%d %d %d %d\n",i,j,A,B);
}
cc++;
}
else if(cc==2){
break;
}
}
}
if(inx1==-1&&inx2==-1){
sort(a,a+n);
inx1=a[0].inx;
inx2=a[1].inx;
}
if(inx1>inx2) swap(inx1,inx2);
printf("%d %d\n",inx1+1,inx2+1);
}