zoukankan      html  css  js  c++  java
  • 随缘练习2

    P5657 格雷码

    找规律题

    首先先记下这个格雷码的生成方法

    有个很重要的结论有k位时

    如果自左往右编号

    那么第i号是( i XOR i/2 )以2进制表示

    洛谷P3811

    记忆化求逆元,递推

    https://www.luogu.com.cn/problemnew/solution/P3811

    用于求一连串数字对于一个(mod p)的逆元

    只能用这种方法,别的算法都比这些要求一串要慢。

    那么我们如何递推嘞?

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    #define INF 1e10+5
    #define maxn 30000005
    #define minn -105
    #define ld long double;
    #define uint unsigned int;
    #define ull unsigned long long;
    typedef long long ll;
    #define re register
    inline int read()
    {
        re int t=0;
        re char v=getchar();
        while(v<'0')v=getchar();
        while(v>='0')
        {
            t=(t<<3)+(t<<1)+v-48;
            v=getchar();
        }
        return t;
    }
    int invn[maxn];
    int main()
    {
        cin.tie(0);
        cout.tie(0);
        ios_base::sync_with_stdio(false);
        int n,p;
        n=read();p=read();
        invn[1]=1;
        cout<<1<<endl;
        for(int i=2;i<=n;i++)
        {
            invn[i]=(ll)(p-p/i)*invn[p%i]%p;
            cout<<invn[i]<<'
    ';
        }
        return 0;
    }
    

    Xidian OJ 1038 DP

    http://acm.xidian.edu.cn/problem.php?id=1038

    原题题意:

    我只提供少量输入数据

    https://www.luogu.com.cn/paste/r1tqrhlt

    虽热数据比较小,这题真的很坑爹,状态转移比较难找

    思路:

    注意第一组数据

    4 3
    0 1 1
    0 1 1
    1 1 0
    1 1 0
    

    显然状态的转移要基于前面行数满排的情况

    但是,是跨几行嘞?

    显然无法确定

    3 5
    0 0 0 1 1
    0 1 1 1 1
    0 1 1 0 0
    

    所以状态不能通过简单的方法表示

    故我们考虑采用状态压缩

    dp数组表示某行使用某个决策

    每一个决策,使用状态压缩:

    用二进制位的有无来判断当前位置是否被选择,1表示此位置可选,0表示此位置不可选

    但是这个位置会不会真的选上,我们不关注,我们用dp表示的只是可选性

    那么n,m的范围比较小

    我们可以暴力枚举0->1023这些可能的决策状态

    首先,如果这个位置我们的决策认为它为1,即可选,但是该位置的玻璃损坏了,显然形成矛盾,直接continue

    扫完一遍发现,ok,没问题,那么我们开始考虑这些点位如何进行贪心原则

    我们要先将所有比他小的这些幂集的答案更新上去,因为他们可选择的位点更少,所以当前决策的dp值肯定大于等于他们,并且由于我们是0->1023去枚举,那么他就一定提前被更新掉了

    其次,也是这道题目的核心,如何找比上述更优的值嘞?

    其实无非我们现在就要保证这个决策cur值转成2进制后它是所有11是成对出现的

    抓满足上述这句话的决策来更新

    如110110,11110110,1100011这样

    拿任意一对11而言,根据贪心原则

    只要是上一行对应的两个位置可取1,那么我们就从上一行那两个位置决策为00的状态转移过来并+1(上一个决策你选00,就默认它锁定没有被选,但它本身是好的,这一行才被决策取走)

    到这,有人可能会产生疑惑?

    如111这样的三个连续的会被更新到最优值吗?显然因为(11)0,和0(11)在之前更新过了,所以只要1以奇数形式连续,它的最优状态一定在之前出现过并且在之前步骤中转移给了它

    但是又有人问如1111,我前两个11,后两个11,都不能拼成2*2,但是我中间两个可以,这不会忽略掉最优解吗?显然不会,跟上面问题同理,0110状态在之前以及被更新过了,所以在前面的步骤已经将最优解给力当前值

    至此,我们还有最后一个问题,在进行成对更新的过程中,我们算完了可更新的对数,那么前继状态该如何选择?

    前继承状态一定是可选位数越多越好,即非违法状态下的最大值,

    而这个最大值上,为1的bit,也表示了当前位置的可选性

    一旦我们11这一对被选上了,说明前面这里刚好被挖掉成了00,

    所以我们统计一个sum,累加每次11代表的值,最后取上一行最大值与sum的异或,就是前继状态了

    ac:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<vector>
    using namespace std;
    #define INF 0x3f3f3f3f
    #define maxn 15
    #define minn -105
    #define ll long long int
    #define ull unsigned long long int
    #define uint unsigned int
    inline int read()
    {
        int ans=0;
        char last=' ',ch=getchar();
        while(ch<'0'|ch>'9')last=ch,ch=getchar();
        while(ch>='0'&&ch<='9')ans=ans*10+ch-'0',ch=getchar();
        if(last=='-')ans=-ans;
    	    return ans;
    }
    int dp[maxn][1025];
    int a[maxn][maxn];
    int curmax[maxn];
    int n,m;
    vector<int>line;
    void dfs(int row,int cur,int index,int sum)
    {
        dp[row][cur]=max(dp[row][cur],dp[row][cur-sum]);
        if(index==line.size())return;
        dfs(row,cur,index+1,sum+line[index]);
    	dfs(row,cur,index+1,sum);
    }
    void _DP(int row,int cur)
    {
        int num=0,sum=0;
        for(int i=0;i<m;i++)
        {
            if(cur&(1<<i))
            {
            if(a[row-1][i]&&a[row-1][i+1])
                {
                    num++;
                    sum+=3<<i;
                }
                i++;
            }
        }
    	dp[row][cur]=max(dp[row-1][curmax[row-1]^sum]+num,dp[row][cur]);
    }
    void _renew(int row,int cur)
    {
        bool ok=1,check=0;
        for(int i=0;i<m;i++)
        {
            if(check&&!(cur&(1<<i)))ok=0;
            if(cur&(1<<i))
            {
                line.push_back((1<<i));
                check=!check;
            }
        }
        if(check)ok=0;
        dfs(row,cur,0,0);
        line.clear();
        if(ok)_DP(row,cur);
    }
    void solve()
    	{
        memset(dp,0,sizeof(dp));
        memset(a,0,sizeof(a));
        memset(curmax,0,sizeof(curmax));
        cin>>n>>m;
        for(int i=1;i<=n;i++)
            for(int j=0;j<m;j++)
           		cin>>a[i][j];
        int ans=0;
        for(int i=2;i<=n;i++)
        {
            for(int cur=0;cur<1024;cur++)
            {
                bool ok=1;
                for(int t=0;t<10;t++)
                {
                    if(cur&(1<<t))
                        if(!a[i][t])ok=0;
                }
                if(!ok)continue;
                curmax[i]=cur;
                _renew(i,cur);
                ans=max(ans,dp[i][cur]);
                //cout<<i<<" "<<cur<<" "<<dp[i][cur]<<endl;
            }
           //cout<<endl<<endl;
        }
            cout<<ans<<endl;
    }
    int main()
    {
        int _t;
        _t=read();
        while(_t--)solve();
        return 0;
    }
  • 相关阅读:
    Django_05_模板
    Django_04_视图
    Django_03_后台管理
    Django_02_创建模型
    Django_01_创建图书管理项目
    Djang简介
    day_03比特币转账的运行原理
    day_02比特币的转账机制及其7个名词
    day01_人类社会货币的演变
    Socket问题
  • 原文地址:https://www.cnblogs.com/et3-tsy/p/12861265.html
Copyright © 2011-2022 走看看