Petrozavodsk Winter-2018. Jagiellonian U Contest E.Guessing Game【三进制状压DP】

题意:和UVA1252一样,n个长为k的01串,问最优策略下最多猜几次就可以知道是哪个串;

分析:UVA那个题k是11,刚好可以暴搜,但是这个题是13,就要想想状压DP,但这个每一位有三种状态,已定为1,已定为2,未定,所以就是三进制下的状压DP,就可以先预处理出所有状态三进制下每一位的值和3^i(p[i]);读入过程中处理记下出现的状态,记下个数为1,然后扫所有状态,比如状态i的第j位是2,表示这一位未知,那么满足这个状态的数量就是状态(i-p[j])和(i-p[j]*2),就表示第j位已知是1或0的和,处理完之后进行状态转移,碰到状态的未知位置时可以从该位置已知为1或0转移来,结果要再加一,因为判断这一位是1还是0还需要一次操作,更新转移到最后一个状态就是答案;

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define pb push_back
using namespace std;
typedef long long ll;
const int maxn=1600000;
const int mod=1e9+7;
char s[20];
int p[15],val[maxn][14];
int num[maxn],a[15],dp[maxn];
void init()
{
    p[0]=1;
    for(int i=1;i<14;i++) p[i]=3*p[i-1];
    for(int i=0;i<p[13];i++)
    {
        int x=i;
        for(int j=0;j<13;j++) val[i][j]=x%3,x/=3;
    }
}
void rua()
{
    int n,k;scanf("%d%d",&n,&k);
    for(int i=0;i<p[k];i++) num[i]=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s);int x=0;
        for(int j=0;j<k;j++) x=x*3+(s[j]-'0');
        num[x]++;
    }
    for(int i=0;i<k;i++) 
        for(int j=0;j<p[k];j++) 
            if(val[j][i]==2) num[j]+=(num[j-p[i]]+num[j-p[i]*2]);
    for(int i=0;i<p[k];i++)
    {
        if(num[i]<=1) {dp[i]=0;continue;}
        int tmp=INF;
        for(int j=0;j<k;j++) 
            if(val[i][j]==2) tmp=min(tmp,max(dp[i-p[j]],dp[i-p[j]*2]));
        dp[i]=tmp+1;
    }
    printf("%d\n",dp[p[k]-1]);
}
int main()
{
    init();
    int t;scanf("%d",&t);
    while(t--) rua();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43813163/article/details/102507238