陕西省集训day2(二分,贪心,枚举续)

来源:互联网 发布:淘宝卖商品不交保证金 编辑:程序博客网 时间:2024/04/30 06:51

其实是上一篇的续啦,还是那场比赛
http://acm.hust.edu.cn/vjudge/contest/125415#overview

I题:

http://codeforces.com/problemset/problem/442/A

题意:

小明有25种牌,每张有花色和数值,现在他知道手里有哪些牌,但是不知道哪个是哪个。 旁边的人可以提示他花色,把某个花色的牌告诉他,或者数值,同理。求最少提示次数。

思路:

注释很清晰,用二进制代表花色或者数字是否已知

#include<cstdio>#include <iostream>using namespace std;int n;char a[110][6];int b[110][2];int ans;int change(int x)   //换成二进制数后,各个位置加起来表示告诉了多少个提示{    int res=0;    while(x)    {        res+=x%2;        x/=2;    }    return res;}int main(){    int i,j,k,l;    scanf("%d",&n);    for(i=0; i<n; i++)    {        scanf("%s",a[i]);        if(a[i][0]=='R')b[i][0]=0;        else if(a[i][0]=='G')b[i][0]=1;        else if(a[i][0]=='B')b[i][0]=2;        else if(a[i][0]=='Y')b[i][0]=3;        else if(a[i][0]=='W')b[i][0]=4;        b[i][1]=a[i][1]-'1';    }    ans=10;   //二进制数字代表字母/数字那5位是否已知,知道的话该位是1,最后多少个1就知道多少个已知提示    for(i=0; i<32; i++)    {        for(j=0; j<32; j++)        {            for(k=0; k<n; k++)//枚举每一张牌            {               // cout << b[k][0]<<endl;                for(l=k+1; l<n; l++)                {                    //cout <<b[k][0] <<endl;                    if(b[k][0]==b[l][0]&&b[k][1]==b[l][1])continue;  //把后面的与当前位完全相同,字母和数字都相同,不区分,相当于已知                    else if(b[k][0]!=b[l][0]&&(i>>(b[k][0]))%2)continue; //不相同但是k的字母已知                    else if(b[k][0]!=b[l][0]&&(i>>(b[l][0]))%2)continue;//不相同但是l的字母已知                    else if(b[k][1]!=b[l][1]&&(j>>(b[k][1]))%2)continue;//k的数字已知                    else if(b[k][1]!=b[l][1]&&(j>>(b[l][1]))%2)continue;//l的数字位已知                   else                     break;//两个都不知道就惨了                }                if(l!=n)break;//两个都不知道的话说明当前的i,j不符合条件            }            if(k==n)                ans=min(ans,change(i)+change(j) );//所有的牌都符合了条件,全部已知。            }        }    printf("%d",ans);}

L题:

题意:

给出n个数,若有一个区间,只重排那个区间的数字(可不排),使得数组是构成一个回文,则它就是答案之一

思路:

①若本身就是回文,则答案就是1+2+……+n-1+n,即(1+n)*n/2
②判断数字个数,若n为奇数个,则只能有一个数字个数是奇数个,偶数则不能有数字个数是奇数个的。否则肯定构不成回文,答案为0
③从两头往中间判断,相同的把数字个数-2,这些数字不是必须要重排的(可排可不排),当找到两个数字不同的时候,就需要两头各找出一个最小区间,使得他们重排后为回文。

要找最小区间,容易想到二分找,但怎么判断该区间满足条件就比较麻烦。首先得确定二分的下界,若中间有部分数字相同的话,则区间有可能只存在一边或两边各占,下界就是中间开始不同的地方,上界就是③中对应另一个不同的数字位置,若中间数字完全没有相同的话,则一定是两边各占,这样下界就是(n+1)/2,上界也是③中对应另一个不同的数字位置。

至于判断区间是否满足,假设当前区间长度为len1,另一半区间为len2,则只要比较两个区间大小,然后找到小的那个,然后判断该区间是否有数字大于该数字总的一半即为不满足,否则满足。

获得两个最小区间后,用容斥定律来解,假设两个区间为[l1,r1],[l2,r2]。左区间的总方案=l1*(n-r1+1),右区间总方案=(n-r2+1)l1。减去的共同的方案数=l1(n-r2+1)

#include<cstdio>#include<cstring>#include<iostream>#include<map>using namespace std;#define LL long longconst int maxn=100005;map<int,int> mp,mp1,mp2;map<int,int> ::iterator it;struct sw{    int l,r;    sw(){}    sw(int ll,int rr){        l=ll,r=rr;    }}ans[2];int n;int num[maxn];int flag,cou=0,cou2=0,now,key;unsigned LL res=0;bool check(int l,int r){    mp1.clear();    int len1=r-l+1,len2=n-2*now-len1;    if(len2<len1){        if(l==now+1){            r=n-l+1;            l=r-len2+1;        }        else {            l=n-r+1;            r=l+len2-1;        }    }    for(;l<=r;l++)        mp1[num[l]]++;    for(it=mp1.begin();it!=mp1.end();it++){        if(it->second>mp[it->first]/2)            return 0;    }    return 1;   }int main(void){    scanf("%d",&n);    for(int i=1;i<=n;i++){        scanf("%d",&num[i]);        mp[num[i]]++;    }    flag=n%2;    for(it=mp.begin();it!=mp.end();it++){        if(it->second%2) cou++,key=it->first;        if(cou>flag) break;    }    if(cou>flag) {        printf("0\n");        return 0;    }    for(int s=1,e=n;s<e;s++,e--){        if(num[s]!=num[e]){            now=s-1;            int l=1,r=e-s;            for(int i=(n+1)/2,j=(n+2)/2;1;i--,j++)                if(num[i]!=num[j]||(flag&&i==(n+1)/2&&num[i]!=key)){                    key=i-s;                    l=key;                    break;                }            while(l<=r){                int mid=(l+r)>>1;                if(check(s,s+mid))                    r=mid-1;                else l=mid+1;            }            ans[cou2++]=sw(s,s+l);            l=key,r=e-s;            while(l<=r){                int mid=(l+r)>>1;                if(check(e-mid,e))                    r=mid-1;                else l=mid+1;            }            ans[cou2++]=sw(e-l,e);            break;        }        else mp[num[s]]-=2;    }    if(cou2==0){        res=1LL*(n+1)*n/2;        cout<<res<<endl;        return 0;    }    {        int l1,l2,r1,r2;        l1=ans[0].l,l2=ans[1].l;        r1=ans[0].r,r2=ans[1].r;        res+=l1*(n-r1+1)+(n-r2+1)*l2;        res-=l1*(n-r2+1);        cout<<res<<endl;    }    return 0;}
0 0
原创粉丝点击