p1825

本题是一个有趣的有后效性的题目,如果是考察n个数能否等于x大家肯定都会,背包嘛。

但是能否被x整除改怎么写呢?原来的状态不满足后无效性啊。考虑到x较小,最直接的方法就是增加一个维度。

维护一个flag[i][f]表示第i个数时能否组成%x==f,他的转移方程是

flag[0][0]=1;
flag[i][f]=flag[i-1][f-o[i]]||flag[i-1][f+o[i]];

但是f如果小于o[i]呢?如果f+o[i]大于x了呢?就很尴尬。

这个时候就要巧妙的运用模运算了。

其实我是从flag[i-1][f]出发,如果flag[i-1][f]==1,就可以更新加减o[i]的答案了。可以这样写。

if(flag[i-1][f])
    flag[i][((f-t)%x+x)%x]=flag[i][(f+t)%x]=1;

减的时候先不管会不会小于0,就先减一下o[i],然后对x取余。如果是负数也不会小于-x,那就再加上一个x;然而如果减一下还是一个正数的话现在就会大于x,那就再模一次x呗。

加的时候就加一下后对x取余就好了。

 复杂度是O(k*n*x)。

int i,f,t;
int k,n,x;
bool flag[21000][210];
int main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
//freopen("123.in","r",stdin);
//freopen("123.out","w",stdout);
    cin>>k;
    for(;k>0;k--)
    {
        cin>>n>>x;
        memset(flag,0,sizeof(flag));
        flag[0][0]=1;
        for(i=1;i<=n;i++)
        {
            cin>>t;
            for(f=0;f<x;f++)
                if(flag[i-1][f])
                    flag[i][((f-t)%x+x)%x]=flag[i][((f+t)%x+x)%x]=1;
        }
        if(flag[n][0])cout<<"Divisible"<<endl;
        else          cout<<"Not divisible"<<endl;
    }
}

 

posted @ 2018-08-11 14:23  zzuqy  阅读(153)  评论(0编辑  收藏  举报