翻转硬币

版权声明:未经过同意不得转载 https://blog.csdn.net/qq_42500298/article/details/89515884

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们要求的最后状态就是:
a 1 = a m + 1 a_1=a_{m+1}
a 2 = a m + 2 a_2=a_{m+2}
a i = a m + i = a 2 m + i = . . . . . a_i=a_{m+i}=a_{2m+i}=.....
直到下标超过n
做一个转换,让每m个一组
就变成了
1 2 3 … m
m+1 m+2 m+3 … 2m
… … … …

翻转1个硬币就是把矩阵中的一个0变1 1变0
翻转矩阵中的前k行
那么对于同样的k执行操作是有意义的吗?
显然是没有意义的,例如 k=2 那就把前2m个操作异或1,再异或1,就是取反再取反,就回到原来状态了
那翻三次呢,显然就相当于翻一次
翻多次是没有意义的,对于相同的k最多执行1次

于是

当m>=√n时

行数<=√n≈18
kmax=18
枚举行的最后状态,枚举每一次k执不执行操作
暴力取把花费的代价取min

当m<=√n时

列数18行
这时行数很多很多,所以枚举列的最后状态

考虑状压DP

f[i][j][k]表示执行第i行的状态为j当前是否翻转状态
从前往后每个子任务斗鱼前一个有联系,有后效性,所以从后往前推,看k于j有多少个不同,只要是不同就是第一个状态

#include <bits/stdc++.h>
using namespace std;
const int MAX_N=305;
int n,m;
string s;
namespace subtask_1
{
 int f[400][300000][2];
 int main()
 {
  for(int i=0;i<n;i++)s[i]-='0';
  int ans=1<<30;
        int k=n/m;
        if(n%m)k++;
        for(int i=0;i<(1<<m);i++)
        {
            for(int j=(k-1)*m;j<n;j++)
            {
                if((s[j]<<(j%m))^(i&(1<<(j%m))))f[k-1][i][0]++;
                else f[k-1][i][1]++;
            }
            for(int kk=k-2;kk>=0;kk--)
            {
                for(int j=kk*m;j<(kk+1)*m;j++)
                {
                    if((s[j]<<(j%m))^(i&(1<<(j%m))))f[kk][i][0]++;
                    else f[kk][i][1]++;
                }
                f[kk][i][0]+=min(f[kk+1][i][0],f[kk+1][i][1]+1);
                f[kk][i][1]+=min(f[kk+1][i][1],f[kk+1][i][0]+1);
            }
            ans=min(ans,min(f[0][i][0],f[0][i][1]));
        }
        printf("%d\n",ans);
        return 0;
 }
}
namespace subtask_2
{
 int siz;
 int B[MAX_N][MAX_N];
 int cnt[MAX_N][2];
 void flip(int row)
 {
  for(int i=0;i<m;i++)
  {
   if(B[row][i]==-1)continue;
   B[row][i]^=1;
  }
 }
 int main()
 {
  memset(B,-1,sizeof(B));
  siz=n/m;
  int ans=INT_MAX;
  for(int i=0;i<(1<<siz);i++)
  {
   for(int j=0;j<n;j++)B[j/m][j%m]=s[j]-'0';
   for(int j=0;j<siz;j++)
   {
    if(i&(1<<j))
    {
     for(int k=0;k<=j;k++)
     {
      flip(k);
     }
    }
   }
   for(int j=0;j<n;j++)cnt[j%m][B[j/m][j%m]]++;
   int sum=__builtin_popcount(i);
   for(int i=0;i<m;i++)sum+=min(cnt[i][0],cnt[i][1]);
   ans=min(ans,sum);
  }
  cout<<ans<<endl;
  return 0;
 }
}
int main()
{
 cin>>s>>m;
 n=s.size();
 if(m*m<=n)subtask_1::main();
 else subtask_2::main();
 return 0;
} 

猜你喜欢

转载自blog.csdn.net/qq_42500298/article/details/89515884
今日推荐