牛客练习赛41(A,B,C,D)

A.知识点:博弈论

题解思路:

第一种情况,n=m,显然“Yes”

第二种情况,n!=m;把n分为就奇偶两种情况:(1)n为奇数,要将所有硬币全部变成反面,等价于所有的回合数翻硬币的次数之和是奇数,那么即使Bob什么都不做,Alice无论怎么做,都不会达到目的 (2)n为偶数,等价于总数和为偶数,那么,Alice有可能完成,这时候,Bob只要在第一次把任意一个硬币翻过来,这个时候的情况就和(1)一样了。

所以,n=m,yes

n!=m,no

 

B.知识点:动态规划

dp[i][j]表示在第i个回合,构成数字i的情况数。

由此,状态转移方程就是:dp[i] [j] = dp[i-1] [j-a[i] ] +dp[i-1] [-j]

注意要加两个东西:

(1):滚动数组 (二维存不下)

(2)排除所有j="666"的情况

 

由于j和-j以及j-a[i]的正负的不确定性,只用一个dp数组滚动肯定不行,因此要加上一个辅助数组来保存上一个回合dp值

code如下:

#include
#define register int rint
#define INF 0x3f3f3f3f3f
#define MOD 100000007
#define mem(a,b) memset(a,b,sizeof(a))
#define PI 3.141592653589793
using namespace std;
typedef long long ll;
typedef pairPII;
const int N=400000;
const int Max=1e4+20;
const double esp=1e-8;
inline int rd() {
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
int n,a[N],dp[N+200],tmp[N+200];
int main()
{
    n=rd();
    for(int i=1;i<=n;++i)   a[i]=rd();
    mem(dp,0);

    dp[199800]=1;
    for(int i=1;i<=n;++i)
    {
        mem(tmp,0);
        for(int j=0;j<=399600;++j)
        {
            int real=j-199800;
            if(real==666)   continue;
            tmp[j]=(dp[399600-j]+tmp[j])%MOD;
            tmp[j]=(dp[j-a[i]]+tmp[j])%MOD;
        }
        //cout<

 

C.知识点:并查集

1.把所有节点变成并查集森林

2.把每一棵树的a【i】总和求出+排序

3.加和前m个,即为所求

 

D.知识点:思维+bfs

题解思路:有n个数,每个数m位,那么最多有2^m种情况,每一种遍历再和n个数异或的话,时间复杂度就是n*2^m,太大了。

让我们换一种思路来看,把每一位是0还是1看成一种状态,那么就有m种状态,现在把这m个位想象成一个图,起点是数字x,终点可能是(1111...)m个1(可能不到)。那么我们每次改变一个位,然后标记我们走过的路径的长度,这样就类似于bfs的做法(一边标记,一边拓展)。最总我们用这n个数能走过的最大路径长度*-1+m就是答案了。

 

code(来自官方题解:https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40380369)

#include
using namespace std;
int deep[1<<22];
void solve(){
    queue q;
    int n,m,ans=0;
    char s[30];
    scanf("%d %d",&n,&m);
    memset(deep,-1,sizeof(int)*(1<<(m+1)));
    for(int i=0;i

 

你可能感兴趣的:(数论,动态规划,并查集,搜索)