zoukankan      html  css  js  c++  java
  • Codeforces 1042C Array Product

    这道题还是挺有意思,本人比赛时候上没做出来(英语不好漏看一个条件......)

    题意描述:

    给定一个数组(可能有正数,负数和0),可以进行如下两种操作:

    1.选择两个位置 $ i $ , $ j $ , $ i $ 在 $ j $ 左边。把 $ j $ 处的数字 $ a[j]*=a[i] $ ,并把 $ a[i] $ 删除掉。

    2.选择一个位置 $ x $ ,将这个位置的数字 $ a[x] $ 删除掉。该操作只能用一次。

    显然,通过这两个操作,可以把数组中的数字处理到只剩一个。要求使最后的这个数字尽量大。

    输出操作的序列。

    $ (1 i j) $ 表示操作1

    $ (2 x) $ 表示操作2

    题解:

    开到这一题的时候有点慌,但是想了想就发现,有一些很方便的性质:

    ·正数直接乘到一起

    ·如果有0的话,直接删掉就好。

    ·如果有偶数个负数,无需考虑删去,直接一个一个乘起来就好,最终得到的结果是正数;

    ·如果有奇数个负数,那么就选择「绝对值最小的负数」也就是「最大的负数」删去,剩下的仍然是偶数个负数,且这样结果最大。

    以上就是我在比赛时候想到的......过了样例直接拿去交,然后就WA掉了......QAQ

    比赛结束后旁边的dalao告诉我「删除操作只能用一次诶」

    好吧是我英语不好没看懂。

    然后怎么做?

    考虑要删除一些位置的数,我们可以先把这些位置由小到大排序,再通过操作1,把这些数集中到最末的一位上,然后对最后一个位置进行操作2删除就可以了。

    迎刃而解。

    代码实现:

    准备工作

    
    set<int>removed;//记录某个位置的元素是否被删除了
    int n,a[200050];//输入
    struct ope{//operation,操作
    	int c,l,r;//操作种类和位置
    	ope(int c=0,int l=0,int r=0):
    		c(c),l(l),r(r){
    			
    		}
    };
    ope os[200050];//记录操作
    int cnt=0;
    void v1(int l,int r){//操作1
    	cnt++;
    	os[cnt]=ope(1,l,r);
    	removed.insert(l);
    }
    void v2(int x){//操作2
    	cnt++;
    	os[cnt]=ope(2,x,0);
    	removed.insert(x);
    }
    int num_fu=0;//负数的个数
    int max_fu_pos=0,max_fu=0;//用来找最大的负数的位置
    int tr[200050],cnt2=0;//tr=to_remove,预先将所要删除的位置放在这里
    void rm(int x){//将某个位置丢进to_remove中
    	cnt2++;
    	tr[cnt2]=x;
    }
    bool cmp(int x,int y){//排序用
    	return x<y;
    }
    
    

    读入,检查负数个数,判断是否删去最大负数,删去0和最大负数(如果需要)

    
            scanf("%d",&n);
    	for(int i(1);i<=n;i++){
    		scanf("%d",&a[i]);
    		if(a[i]<0){//如果是负数
    			num_fu++;
                    //更新最大负数位置
    			if(max_fu==0)max_fu=a[i],max_fu_pos=i;
    			else if(a[i]>max_fu)max_fu=a[i],max_fu_pos=i;
    		}
    		if(a[i]==0)rm(i);//删去0
    	}
    	if(num_fu%2){//如果需要删去最大负数
    		rm(max_fu_pos);
    	}
    	if(cnt2){//因为有可能出现cnt2==0,即「不需要删去什么」的情况
    		sort(tr+1,tr+1+cnt2,cmp);//从小到大排序
    		for(int i(1);i<=cnt2-1;i++)v1(tr[i],tr[i+1]);//从前往后合并
    		v2(tr[cnt2]);//删除最末一个
    	}
    
    

    合并剩下的负数和正数,记录操作

    
            int lst=0;//指向上一个的位置
    	for(int i(1);i<=n;i++){
    		if(removed.count(i))continue;//如果这一位已经被删了(0或最大负数),跳过
    		if(lst==0){
    			lst=i;
    			continue;
    		}
    		v1(lst,i);
    		lst=i;//更新指针
    	}
    
    

    注意:数据可能会出现一种情况,就是「删去所有0和最大负数后,数组已经空了」

    比如:

    4
    0 -10 0 0

    这样的话,如果把所有要删除的合并,然后删掉最末一个,就会出现「一个也不留」的情况。

    所以在合并所有数字之后,输出操作序列之前,要加一个特判:

    如果是「一个也不留」的情况,就不执行「删除最末一个」的操作,留下一个0作为答案。

    
            bool flag=1;//是否全为0 
    	for(int i(1);i<=n;i++){
    		if(a[i]!=0 and !removed.count(i)){//如果有一个数非零且没有被删掉,
    			flag=0;
    			break;
    		}
    	}
    
    

    最后是输出答案:

    
            for(int i(1);i<=cnt;i++){
    		if(os[i].l==0)continue;
    		if(os[i].c==1)printf("%d %d %d
    ",os[i].c,os[i].l,os[i].r);
    		else if(!flag)printf("%d %d
    ",os[i].c,os[i].l);//如果是上述情况就不输出操作2
    	}
    
    

    到此结束。

    完整代码:

    
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<set>
    #define LL long long
    using namespace std;
    set<int>removed;
    int n,a[200050];
    struct ope{
    	int c,l,r;
    	ope(int c=0,int l=0,int r=0):
    		c(c),l(l),r(r){
    			
    		}
    };
    ope os[200050];
    int cnt=0;
    void v1(int l,int r){
    	cnt++;
    	os[cnt]=ope(1,l,r);
    	removed.insert(l);
    }
    void v2(int x){
    	cnt++;
    	os[cnt]=ope(2,x,0);
    	removed.insert(x);
    }
    int num_fu=0;
    int max_fu_pos=0,max_fu=0;
    int tr[200050],cnt2=0;
    void rm(int x){
    	cnt2++;
    	tr[cnt2]=x;
    }
    bool cmp(int x,int y){
    	return x<y;
    }
    int main(){
    	scanf("%d",&n);
    	for(int i(1);i<=n;i++){
    		scanf("%d",&a[i]);
    		if(a[i]<0){
    			num_fu++;
    			if(max_fu==0)max_fu=a[i],max_fu_pos=i;
    			else if(a[i]>max_fu)max_fu=a[i],max_fu_pos=i;
    		}
    		if(a[i]==0)rm(i);
    	}
    	if(num_fu%2){
    		rm(max_fu_pos);
    	}
    	if(cnt2){
    		sort(tr+1,tr+1+cnt2,cmp);
    		for(int i(1);i<=cnt2-1;i++)v1(tr[i],tr[i+1]);
    		v2(tr[cnt2]);
    	}
    	
    	int lst=0;
    	for(int i(1);i<=n;i++){
    		if(removed.count(i))continue;
    		if(lst==0){
    			lst=i;
    			//printf("ans init=%d
    ",ans);
    			continue;
    		}
    		v1(lst,i);
    		//printf("ans*=%d
    ",a[i]);
    		lst=i;
    	}
    	bool flag=1;
    	for(int i(1);i<=n;i++){
    		if(a[i]!=0 and !removed.count(i)){
    			flag=0;
    			break;
    		}
    	}
    	//printf("ans=%d
    ",ans);
    	for(int i(1);i<=cnt;i++){
    		if(os[i].l==0)continue;
    		if(os[i].c==1)printf("%d %d %d
    ",os[i].c,os[i].l,os[i].r);
    		else if(!flag)printf("%d %d
    ",os[i].c,os[i].l);
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    记录爱忘记的STL知识点
    小狼
    CVTE总结
    STL底层实现
    小狼,你家BOSS喊你面试啦!!!(四)
    npm属性笔记
    iview中关于table组件内放入Input会失去焦点
    js实现数组内数据的上移和下移
    google搜索使用技巧
    bat中实现代码拷贝到指定目录后启动命令行并更改默认路径
  • 原文地址:https://www.cnblogs.com/soul-M/p/9666952.html
Copyright © 2011-2022 走看看