您的位置:首页 > 其它

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

2016-08-20 16:41 369 查看
其实是上一篇的续啦,还是那场比赛

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 long
const 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: