题意:给定N个二进制数,求出一个二进制数使得其和N个二进制数的complaint之和最小。complaint=不相同的bit的个数。另外有M个constraint,求出的二进制数不能出现在constraint中。
如果没有constraint,最优解opt贪心即可求出来。假设sum[p]是N个二进制数bit p的1的个数之和,那么opt[p]=max(sum[p],N-sum[p])。
入手点是M<100,开始认为是从opt贪心,枚举改变一个bit,改变两个bit,...直到不出现在constraint中。如果改变一个bit,直接贪心找到complaint最大的一个bit取反即可。但是如果改变两个bit,只能用搜索了,因为给定一个数组x1,x2,x3,x4,x5,(e.g., 5, 4, 3, 2, 1) 如果从x1开始枚举,可能满足constraint的组合里x1+x5满足条件,但是有可能x2+x3 complaint更大。所以感觉还是要枚举2^p个组合。。。
也想过按照bit DP,但是当时觉得前p个bit满足constraint这个不好弄,因为constraint是个global的东西。。真是行百里者半九十。
正解是按照bit构造。因为只有M个constraint,所以只要找到complaint最小的前M+1个二进制数,一定有一个是符合条件的。假设s[0,...,p+1]是前M+1个,那么s[0,..,p]一定也是前M+1个。Proof by contradiction: 如果s[0,...,p+1]是前M+1个而s[0,...,p]不是,假设s[p+1]增加的complaint是x,那么排在s[0,...,p]之前的s'[0,...,p]增加一个相同的bit (s[p+1]),构成的新的二进制数的complaint一定比s[0,...,p+1]小,如此可以产生多余M+1个complaint比s[0,..,p+1]小的二进制数,和s[p+1]是前M+1个矛盾。
Then 如果s[0,...,p]不是前M+1个,s[0,..,p+1]一定也不是前M+1个,无论新增的Bit是0 or 1.所以对于每个bit p,维护前M个s[0,..,p],对下一个bit增加0 or 1,再对bit p+1排序找出前M+1个……
可以预处理求出N个二进制数bit p的1的个数之和,这样从bit p to bit p+1可以O(1)求出新增的complaint。
#include<iostream>
#include<stdio.h>
#include<cstdio>
#include<string>
#include<cmath>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
const int maxn=5010;
int T;
int N;
int K;
int A[maxn];
int ans;
int vis[maxn];
int main()
{
// freopen("input.txt","r",stdin);
freopen("A-large.in","r",stdin);
freopen("A.txt","w",stdout);
clock_t START_TIME;
clock_t FINISH_TIME;
START_TIME=clock();
scanf("%d",&T);
for(int ca=1;ca<=T;ca++)
{
memset(A,0,sizeof(A));
memset(vis,0,sizeof(vis));
scanf("%d %d",&N,&K);
ans=0;
for(int i=0;i<N;i++)
{
scanf("%d",&A[i]);
}
sort(A,A+N);
int idx=0;
for(int i=0;i<N;i++)
{
int cnt=0;
while(cnt<K)
{
if(vis[idx]==1)
{
idx++;
}
else if(A[idx]-i-1<0)
{
idx++;
}
else
{
cnt++;
vis[idx++]=1;
ans++;
}
if(idx>=N)
{
break;
}
}
if(idx>=N)
{
break;
}
}
printf("Case #%d: %d\n",ca,ans);
cerr<<"finish case "<<ca<<endl;
}
FINISH_TIME=clock();
cerr<<1.0*(FINISH_TIME-START_TIME)/CLOCKS_PER_SEC <<" (s) "<<endl;
return 0;
}
附暴力code。。
#include<iostream>
#include<stdio.h>
#include<cstdio>
#include<string>
#include<cmath>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
const int maxn=110;
int T;
int N;
int M;
int P;
int mp[maxn][maxn];
int sp[maxn][maxn];
int opt[maxn];
int colsum[maxn];
int arr[maxn];
int complaint[maxn];
int tmparr[maxn];
int ans;
bool cmp(int a,int b)
{
return complaint[a]>complaint[b];
}
bool check()
{
for(int i=0;i<M;i++)
{
bool flg2=true;
for(int j=0;j<P;j++)
{
if(opt[j]!=sp[i][j])
{
flg2=false;
break;
}
}
if(flg2==true)
{
return true;//exist the same one
}
}
return false;
}
bool checktmp()
{
for(int i=0;i<M;i++)
{
bool flg2=true;
for(int j=0;j<P;j++)
{
if(tmparr[j]!=sp[i][j])
{
flg2=false;
break;
}
}
if(flg2==true)
{
return true;//exist the same one
}
}
return false;
}
void dfs(int n)
{
if(n==P)
{
// for(int i=0;i<P;i++)
// {
// cout<<tmparr[i]<<" ";
// }
// cout<<endl;
// return;
if(checktmp()==true)
{
// cout<<"existing ";
// for(int i=0;i<P;i++)
// {
// cout<<tmparr[i]<<" ";
// }
// cout<<endl;
return;
}
else
{
int tmp=0;
for(int j=0;j<P;j++)
{
if(tmparr[j]!=opt[j])
{
tmp+=max(colsum[j],N-colsum[j]);
}
else
{
tmp+=min(colsum[j],N-colsum[j]);
}
}
// for(int i=0;i<P;i++)
// {
// cout<<tmparr[i]<<" ";
// }
// cout<<endl;
// cout<<tmp<<endl;
ans=min(ans,tmp);
}
return;
}
tmparr[n]=0;
dfs(n+1);
tmparr[n]=1;
dfs(n+1);
tmparr[n]=0;
return;
}
int main()
{
// freopen("input.txt","r",stdin);
freopen("B-small-attempt0.in","r",stdin);
freopen("B-small.txt","w",stdout);
clock_t START_TIME;
clock_t FINISH_TIME;
START_TIME=clock();
scanf("%d",&T);
for(int ca=1;ca<=T;ca++)
{
memset(mp,0,sizeof(mp));
memset(sp,0,sizeof(sp));
memset(opt,0,sizeof(opt));
memset(colsum,0,sizeof(colsum));
memset(arr,0,sizeof(arr));
memset(complaint,0,sizeof(complaint));
memset(tmparr,0,sizeof(tmparr));
ans=0x3f3f3f3f;
scanf("%d %d %d",&N,&M,&P);
for(int i=0;i<N;i++)
{
char tmp[maxn];
scanf("%s",&tmp);
for(int j=0;j<P;j++)
{
mp[i][j]=tmp[j]-'0';
// scanf("%d",&);
// cout<<mp[i][j]<<" ";
}
// cout<<endl;
}
for(int i=0;i<M;i++)
{
char tmp[maxn];
scanf("%s",&tmp);
for(int j=0;j<P;j++)
{
sp[i][j]=tmp[j]-'0';
// scanf("%d",&sp[i][j]);
// cout<<sp[i][j]<<" ";
}
// cout<<endl;
}
for(int j=0;j<P;j++)
{
for(int i=0;i<N;i++)
{
colsum[j]+=mp[i][j];
}
if(colsum[j]*2<=N)
{
opt[j]=0;
}
else
{
opt[j]=1;
}
// arr[j]=j;
// complaint[j]=min(colsum[j],N-colsum[j]);
}
// for(int j=0;j<P;j++)
// {
// cout<<colsum[j]<<" ";
// }
// cout<<endl;
// for(int j=0;j<P;j++)
// {
// cout<<opt[j]<<" ";
// }
// cout<<endl;
// cout<<"dfs"<<endl;
dfs(0);
// sort(arr,arr+P,cmp);
// for(int i=0;i<P;i++)
// {
// if(ckeck()==true)
// {
//
// }
// }
printf("Case #%d: %d\n",ca,ans);
cerr<<"finish case "<<ca<<endl;
}
FINISH_TIME=clock();
cerr<<1.0*(FINISH_TIME-START_TIME)/CLOCKS_PER_SEC <<" (s) "<<endl;
return 0;
}