18.10.4 考试总结

这道题就是一道肥!肠!裸!的!轮廓线dp 然后因为细节太多了还因为有一个sbsbsb编译错误

就是不准我函数名字取count...我恨 我永远讨厌轮廓线dp

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1e6 + 5;
int dp[2][N],n,m,len[200],cnt[N],ans;
char s[200][40];

void Init( ) {

    scanf("%d%d",& n,& m);
    for(int i = 1;i <= n;i ++) {
        scanf("%s", s[i] + 1);
        len[i] = strlen(s[i] + 1);
    }
    for(int i = 0;i < (1 << m);i ++) {
        int w = 0; int j = i;
        while(j) { if(j & 1) w ++; j >>= 1;}
        cnt[i] = w;
    }
}

void Solve( ) {

    int now = 0;
    for(int i = 0;i <= m;i ++) s[0][i] = ‘#‘;
    for(int i = 1;i < n * m;i ++) {
        now ^= 1;
        int row = i / m,col = i % m;
        memset(dp[now ^ 1], 0, sizeof(dp[now ^ 1]));
        for(int sta = 0;sta < (1 << m);sta ++) {
            int high = sta >> (m - col),low = sta ^ (high << (m - col));
            int lnum = cnt[low],hnum = cnt[high],del = 0;
            if(lnum > len[row] || lnum + col < len[row] || hnum > len[row + 1] || hnum + m - col < len[row + 1]) continue;
            if(hnum < len[row + 1]) {
                if((sta & (1 << (m - 1))) && s[row + 1][hnum] == s[row + 1][hnum + 1]) del += 2;
                if((sta & 1) && s[row + 1][hnum + 1] == s[row][len[row] - lnum + 1]) del += 2;
                dp[now ^ 1][(sta >> 1) | (1 << (m - 1))] = max(dp[now ^ 1][(sta >> 1) | (1 << (m - 1))],dp[now][sta] + del);
            }
            dp[now ^ 1][sta >> 1] = max(dp[now ^ 1][sta >> 1], dp[now][sta]);
        }
    }
    for(int i = 0;i < (1 << m);i ++)
        ans = max(ans, dp[now ^ 1][i]);
    printf("%d\n",ans);
}

int main( ) {

    freopen("group.in","r",stdin);
    freopen("group.out","w",stdout);
    Init( );
    Solve( );
}

这道题挺难的 是一道中国剩余定理的题目

本来的公式还是比较好推出来的 先将终点的坐标变为正数

考虑要走到终点 向左向右向上向下的步数相互抵消之后 总共是向右边走$n$步 向上走$m$步

那么枚举向左走的步数$x$ 那么向右边就走了$n + x$步 剩下的向下走的步数就是$y = (t - (n + 2 * x) - m) / 2$

那么方案数就是

      $\sum C_{t}^{x}\cdot C_{t - x}^{n + x}\cdot C_{t - x - x - n}^{y}$

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1e6 + 5;
int dp[2][N],n,m,len[200],cnt[N],ans;
char s[200][40];

void Init( ) {

    scanf("%d%d",& n,& m);
    for(int i = 1;i <= n;i ++) {
        scanf("%s", s[i] + 1);
        len[i] = strlen(s[i] + 1);
    }
    for(int i = 0;i < (1 << m);i ++) {
        int w = 0; int j = i;
        while(j) { if(j & 1) w ++; j >>= 1;}
        cnt[i] = w;
    }
}

void Solve( ) {

    int now = 0;
    for(int i = 0;i <= m;i ++) s[0][i] = ‘#‘;
    for(int i = 1;i < n * m;i ++) {
        now ^= 1;
        int row = i / m,col = i % m;
        memset(dp[now ^ 1], 0, sizeof(dp[now ^ 1]));
        for(int sta = 0;sta < (1 << m);sta ++) {
            int high = sta >> (m - col),low = sta ^ (high << (m - col));
            int lnum = cnt[low],hnum = cnt[high],del = 0;
            if(lnum > len[row] || lnum + col < len[row] || hnum > len[row + 1] || hnum + m - col < len[row + 1]) continue;
            if(hnum < len[row + 1]) {
                if((sta & (1 << (m - 1))) && s[row + 1][hnum] == s[row + 1][hnum + 1]) del += 2;
                if((sta & 1) && s[row + 1][hnum + 1] == s[row][len[row] - lnum + 1]) del += 2;
                dp[now ^ 1][(sta >> 1) | (1 << (m - 1))] = max(dp[now ^ 1][(sta >> 1) | (1 << (m - 1))],dp[now][sta] + del);
            }
            dp[now ^ 1][sta >> 1] = max(dp[now ^ 1][sta >> 1], dp[now][sta]);
        }
    }
    for(int i = 0;i < (1 << m);i ++)
        ans = max(ans, dp[now ^ 1][i]);
    printf("%d\n",ans);
}

int main( ) {

    freopen("group.in","r",stdin);
    freopen("group.out","w",stdout);
    Init( );
    Solve( );
}

这道题就是博弈论了 很容易想到把所有的字符串建成一颗$trie$树  我就直接复制题解了

由于现在获胜目标变为了最后一轮游戏胜利,所以可以有策略地让某一轮输掉或者胜利。

于是现在一个节点的状态变为了四种,由是否拥有选择胜利本轮的能力以及是否拥有选择输掉本轮的能力区分。

对于叶子节点,只有选择输掉的能力。

对于非叶节点,只要有一个子节点不具有选择输掉的能力,该非叶节点便具有选择输掉的能力

只要有一个子节点不具有选择胜利的能力,该非叶节点便具有选择胜利的能力。也就是说,令dp[u] 表示节点u 具有的能力,1 表示具有输掉的能力,2 表示具有胜利的能力,

3 表示两个能力都有,0 则表示两个能力都没有。

那么转移时,dp[u]| = (dp[v]3),其中v 为u 的子节点。

不妨令1 号点为Trie 树的根节点,那么Pure 胜利一局游戏的条件便是:

根节点处同时具有两种能力(这种情况下,前面$n - 1$ 轮一直输,最后一轮胜利即可) 或者根节点处仅具有胜利的能力并且$K$ 为奇数(这种情况下,两个人只能轮流获得每一轮的胜利)。

剩余情况显然是Dirty 获得胜利,例如根节点处仅具有输掉的能力(Pure 只能一直输,显然

Dirty 胜利),或者根节点处两者能力都不具有(这种情况下,Dirty 具有控制每一轮游戏胜负的能力,显然Dirty 胜利)。

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;
int son[N][30],dp[N],cnt,tot,head[N],tov[2 * N],nex[2 * N];
int T,K,n;
char s[N];

void insert(char *s, int len) {

    int now = 0;
    for(int i = 1;i <= len;i ++) {
        int nd = s[i] - ‘a‘;
        if(! son[now][nd]) son[now][nd] = ++ cnt;
        now = son[now][nd];
    }
}

void add(int u, int v) {

    tot ++;
    nex[tot] = head[u];
    tov[tot] = v;
    head[u] = tot;
}

void add_edge( ) {

    for(int i = 0;i <= cnt;i ++)
        for(int j = 0;j <= 26;j ++) {
            if(son[i][j]) {add(i, son[i][j]); add(son[i][j], i);}
        }
}

void dfs(int u, int fa) {

    bool tag = false;
    for(int i = head[u];i;i = nex[i]) {
        int v = tov[i];
        if(v == fa) continue;
        dfs(v, u); tag = true;
    }
    if(! tag) {
        dp[u] = 1; return ;
    }
    for(int i = head[u];i;i = nex[i]) {
        int v = tov[i]; if(v == fa) continue;
        if((dp[v] & 2) == 0) dp[u] |= 2;
        if((dp[v] & 1) == 0) dp[u] |= 1;
    }
}

void clear( ) {

    memset(head, 0, sizeof(head));
    tot = 0; memset(dp, 0, sizeof(dp));
    memset(son, 0, sizeof(son)); cnt = 0;
}

void Solve( ) {

    scanf("%d",& T);
    while(T --) {
        clear( );
        scanf("%d%d",& n,& K);
        for(int i = 1;i <= n;i ++) {
            scanf("%s",s + 1);
            insert(s, strlen(s + 1));
        }
        add_edge( );
        dfs(0, 0);
        if(dp[0] == 3) printf("Pure\n");
        else if(dp[0] == 0) printf("Dirty\n");
        else if(dp[0] == 1) printf("Dirty\n");
        else {
            if(K % 2 == 1) printf("Pure\n");
            else printf("Dirty\n");
        }
    }
}

int main( ) {

    freopen("strGame.in","r",stdin);
    freopen("strGame.out","w",stdout);
    Solve( );
}

原文地址:https://www.cnblogs.com/Rubenisveryhandsome/p/9745133.html

时间: 2024-07-30 20:06:57

18.10.4 考试总结的相关文章

18.10.17 考试总结

今天心态崩崩,,,.. 这道题比较简单 因为每一位是单独对答案产生贡献的 所以枚举每一位 用数位dp求出该位是$1$的数量 在求出该位是$0$的 那么答案就是那一位对应的二的幂次再乘以$num1 * num0 * 2$ 每一对会产生两次贡献 代码 #include <bits/stdc++.h> #define rg register #define il inline using namespace std; typedef long long ll; const ll MOD = 1e9

18.10.22 考试总结

我真的我要被我自己给猪死了md T1让输出边长我输出面积硬生生掉了100分 气死 这道题我之前在讲单调栈的时候是讲过的 对于每一个位置 维护一个他的$up$表示以这里为起点只走$1$向上走的高度 然后对于每一行都跑一边求最大矩形的单调栈即可 维护一个单增的 每次弹栈的时候求出以弹栈元素为矩形边长的最大矩形面积 不过这道题是求最大正方形 就改成边长取$min$再取$max$即可 代码 #include <bits/stdc++.h> #define il inline #define rg re

18.10.6 考试总结

这道题就是一道模拟题 也没有什么细节 反正蛮好写的 代码 #include <bits/stdc++.h> using namespace std; const int N = 1005; int hx, hy, lx, ly, now, ma, h; int vis[2 * N][2 * N], T; char opt[105]; void deal(int sta, char opt, int del) { if(sta == 1) { if(opt == 'E' || opt == 'W

[CareerCup] 18.10 Word Transform 单词转换

18.10 Given two words of equal length that are in a dictionary, write a method to transform one word into another word by changing only one letter at a time. The new word you get in each step must be in the dictionary. 这道题让我们将一个单词转换成另一个单词,每次只能改变一个字母,

18.6 负载均衡集群介绍;18.7 LVS介绍;18.9 LVS NAT模式搭建(上);18.10 LVS NAT模式搭建(下)

扩展: lvs 三种模式详解 http://www.it165.net/admin/html/201401/2248.html lvs几种算法 http://www.aminglinux.com/bbs/thread-7407-1-1.html 关于arp_ignore和 arp_announce http://www.cnblogs.com/lgfeng/archive/2012/10/16/2726308.html lvs原理相关的   http://blog.csdn.net/pi9nc/

18.6 负载均衡集群介绍 18.7 LVS介绍 18.8 LVS调度算法 18.9/18.10 L

18.6 负载均衡集群介绍18.7 LVS介绍 18.8 LVS调度算法 18.9/18.10 LVS NAT模式搭建 原文地址:http://blog.51cto.com/13227377/2149682

#10.4考试总结

10.4考试总结 P1017 进制转换 题目里的进制转换跟最开始做的区别就是这个权值是负数......在短除过程中可能会有各种问题..... 我想用短除就必须要解决这个负数的问题:-3/-2=2 要做到这个样子.....系统从来不会让你好过:-3%-2=-1,总会为难你一下.就需要自己想办法来解决这个问题 -m+n%m 这个样子就可以解决了.....然后题目就简单了起来.栈储存结果,或者递归输出随意就好 P1004 方格取数 没有仔细看数据范围.在认证研读了一边以后.....这.....(哔~~

#10.6考试总结

10.6考试总结 P1063 能量项链 其实是一道区间DP题.......应该并不难.就是DP做少了? 莫名不会.实际上仔细想一想就明白了.直接枚举分段点进行计算就可以了. for(int l = 1, r = x; r < n2; ++l, ++r) { for(int k = l; k < r; ++k) { f[l][r] = max_(f[l][r], f[l][k] + f[k + 1][r] + num[l] * num[r + 1] * num[k + 1]); } if(x =

10.11考试总结

10.11考试总结 全是DP实际上发现暴力也能有部分分....... 三角形牧场 DP......当时总是感觉不像啊 需要处理的就是枚举三角形边长可能出现的情况.因为周长在输入端时候就可以确定了,所以只需要通过枚举两条边就可以强行算出第三条边..... 所以就省空间+时间.... f[0][0] = 1; for (int i=1; i<=n; ++i) for(int j=half; j>=0; j--) for(int k=j; k>=0; k--) if(j >= d[i]