“浪潮杯”山东省第九届ACM大学生程序设计竞赛

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LMengi000/article/details/83036526

目录

A. Anagram

题目描述

输入描述:

输出描述:

输入

输出

解析:

代码:

B.Bullet

题目描述

输入描述:

输出描述:

输入

输出

C.Cities

题目描述

输入描述:

输出描述:

输入

输出

D.Dance

题目描述

输入描述:

输出描述:

输入

输出

题意:

题解:

代码:

E. Sequence

题目描述

输入描述:

输出描述:

输入

输出

解析:

代码:

F. Four—tuples

题目描述

输入描述:

输出描述:

输入

输出

解析:

代码:

G.Games:

题目描述

输入描述:

输出描述:

输入

输出

1.题目含义:

2.分析:

3.求解过程:

4.注意:

5.代码:

H.Dominoes:

题目描述

输入描述:

输出描述:

输入

输出

1.题意:

2.思路:

3.代码:



A. Anagram

题目描述

  Orz has two strings of the same length: A and B. Now she wants to transform A into an anagram of B (which means, a rearrangement of B) by changing some of its letters. The only operation the girl can make is to “increase” some (possibly none or all) characters in A. E.g., she can change an ‘A’ to a ‘B’, or a ‘K’ to an ‘L’. She can increase any character any times. E.g., she can increment an ‘A’ three times to get a ‘D’. The increment is cyclic: if she increases a ‘Z’, she gets an ‘A’ again.

For example, she can transform “ELLY” to “KRIS” character by character by shifting ‘E’ to ‘K’ (6 operations), ‘L’ to ‘R’ (again 6 operations), the second ‘L’ to ‘I’ (23 operations, going from ‘Z’ to ‘A’ on the 15-th operation), and finally ‘Y’ to ‘S’ (20 operations, again cyclically going from ‘Z’ to ‘A’ on the 2-nd operation). The total number of operations would be 6 + 6 + 23 + 20 = 55. However, to make “ELLY” an anagram of “KRIS” it would be better to change it to “IRSK” with only 29 operations. You are given the strings A and B. Find the minimal number of operations needed to transform A into some other string X, such that X is an anagram of B.

输入描述:

There will be multiple test cases. For each testcase:

There is two strings A and B in one line.∣A∣=∣B∣≤50. A and B will contain only uppercase letters
from the English alphabet (‘A’-‘Z’).

输出描述:

For each test case, output the minimal number of
operations.

示例1

输入

ABCA BACA

ELLY KRIS

AAAA ZZZZ

输出

0

29

100

解析:

给定两个字符串,假设是ELLY与KRIS,E到K是6,L到R是6,当第二个L到I时,L是比I大的,此时L就要绕到Z,从Z到A,再从A开始到I,这样长度就是23,Y到S同理,长度是20;这样找完之后序列长度之和就是6 +6+23+20=55.这是题目中给出的一种解答。但是题目要求我们找字符间最小的长度,我就把第一个字符串中的每一个字符与第二个字符串中的每一个字符比较,每次都找出最短的长度,然后加在一起即可。

举例:ELLY与KRIS,第一个字符串中的第一个字符E与第二个字符串中的每一个字符比较,找出最少的长度并记录下来;再从第一个字符串中的第二个字符L与第二个字符串中的每一个字符比较,依旧找最小的,就这样依次循环,把长度累加即可。

代码:

#include<stdio.h>

#include<stdlib.h>

#include<iostream>

#include<algorithm>

#include<vector>

#include<string.h>

#include<math.h>

using namespace std;

const int maxn=1<<30;

char str1[55],str2[55];

char s1[55],s2[55];

int vis[100];

int main()

{

         while(scanf("%s%s",str1,str2)!=EOF)

         {

                   memset(vis,0,sizeof(vis));

                   int sum=0;

                   int len1=strlen(str1);

                   int len2=strlen(str2);

                   for(int i=0;i<len1;i++)

                     s1[i]=str1[i];

                   for(int i=0;i<len2;i++)

                     s2[i]=str2[i];

                   for(int i=0;i<len1;i++)

                   {

                            int minn=maxn;

                            int temp;

                            for(int j=0;j<len2;j++)

                            {

                                     if(vis[j]==0)

                                     {

                                               if((s1[i]-'0')-(s2[j]-'0')>0)

                                               {

                                                  if(abs((s1[i]-'0')-(s2[j]-'0')-26)<minn)

                                                  {

                                                            minn=abs((s1[i]-'0')-(s2[j]-'0')-26);

                                                            temp=j;                                    

                                                  }

                                               }

                                               else

                                               {

                                                if(abs((s1[i]-'0')-(s2[j]-'0'))<minn)

                                                  {

                                                            minn=abs((s1[i]-'0')-(s2[j]-'0'));

                                                            temp=j;                                    

                                                  }

                                               }

                                     }

                            }

                            vis[temp]=1;

                            sum=sum+minn;

                   }

                   printf("%d\n",sum);

         }

         return 0;

}

 

B.Bullet

链接:https://www.nowcoder.com/acm/contest/123/B
来源:牛客网

题目描述

In GGO, a world dominated by gun and steel, players are fighting for the honor of being the strongest gunmen. Player Shino is a sniper, and her aimed shot kills one monster at a time. Now she is in an n*n map, and there are monsters in some grids. Each monster has an experience. As a master, however, Shino has a strange self-restrain. She would kill at most one monster in a column, and also at most one in a row. Now she wants to know how to get max experience, under the premise of killing as many monsters as possible.

输入描述:

The first line contains an integer n.  n<=500

Then n lines follow. In each line there are n

integers, and Aij represents the experience of the monster at grid (i,j).

 If Aij=0, there is no monster at grid (i,j).

The experience is the minimal experience of all the monster which are killed.

It guaranteed that the maximum of the experience of the monster is not larger than 10^{9}

输出描述:

One integer, the value of max experience.

示例1

输入

2

2 0

1 8

输出

2

题意:
在GGO,一个以枪支和钢铁为主导的世界,球员们正在争取成为最强大的枪手。玩家Shino是一个狙击手,她的目标射击一次杀死一个怪物。现在她在n * n地图中,并且在某些网格中有怪物。每个怪物都有经验。然而,作为一个大师,诗乃有一种奇怪的自我约束。她最多会杀死一列中的一个怪物,最多也会杀死一行中的一个怪物。现在,她希望知道如何在杀死尽可能多的怪物的前提下获得最大的体验。

思路:

  1.  杀死怪物的数量尽可能多
  2. 使杀死怪物内的最小的经验值最大

明显来看,是二分,求最小最大问题,把消灭的怪兽的数量作为一个衡量的标准,对此二分图求出最大匹配ans。那么ans为最多能消灭的怪物数量。然后二分枚举经验值k,对于所有矩阵内值w[i][j]>=k的,按照上面一样重新建图并求出最大匹配值。如果求出的值等于ans,那么经验值k是合法的。即求出了最大经验值。

AC代码:

匈牙利算法

/**
题目是要我们杀更多的怪兽获得更多的经验
例如:在杀死同样怪兽的数目下:(数字代表经验值) 
{10,10,3,3}:最小经验为 3 
{10, 10, 5, 5}:最小经验为 5 
我们就要取最小经验为 5的。 
**/
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
using namespace std;
const int maxn=550;
int vis[maxn],form[maxn],w[maxn][maxn];
vector<int>G[maxn];
int n,ans;
bool Find(int x) 
{
	/*
	for(int i=0;i<G[x].size();i++)
	{
		if(w[x][i] && vis[v]==0) 
	//判断是否有怪兽并且这个怪兽还没有被标记过(曾想过要杀这个怪兽但是没杀) 
		{
			vis[v]=1;
			if(form[v]==-1 || Find(form[v]))
			{
				form[v]=x;
				return true;
			}
		}
	}
	return false;*/
	for(int i=0;i<G[x].size();i++)
	{
		int v=G[x][i];
		if(!vis[v])
        {
            vis[v]=1;
            if(form[v]==-1 || Find(form[v]) )
            {
                form[v]=x;
                return true;
            }
        }
	}
	return false;
}
int match()//求最大能杀死的怪兽的数目
{
	int all=0;
	memset(form,0xff,sizeof(form));
	for(int i=0;i<n;i++)
	{
		memset(vis,0,sizeof(vis));
		if(Find(i))
		all++;
	}
	return all;
}
int check(int k)
{
	for(int i=0;i<n;i++)
	 G[i].clear();
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
		  if(w[i][j]>=k)
			G[i].push_back(j);
	return match()==ans;
}
int main()
{
	while(scanf("%d",&n)!=EOF)
	{
		for(int i=0;i<n;i++)
		 G[i].clear();
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<n;j++)
			{
				scanf("%d",&w[i][j]);
			}
		}
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<n;j++)
			{
				if(w[i][j])
				{
					G[i].push_back(j);
				}
			}
		}
		ans=match();
		long long int L=0;
		long long int R=1e9;
		while(L<R)
		{
			int mid=(R+L+1)/2;
			if(check(mid))//通过二分把经验求出来
			{
				L=mid;
			}
			else
			{
				R=mid-1;
			}
		}
		printf("%d\n",L);
	}
	return 0;
}

C.Cities

题目描述

There are n cities in Byteland, and the i city has a value a . The cost of building a bidirectional road between two cities is the sum of their values. Please calculate the minimum cost of connecting these cities, which means any two cities can reach each other.

输入描述:

The first line is an integer T(T<= 1e5)
representing the number of test cases.

For each test case, the first line is an integer n(n <= 1e5), representing the number of cities, the
second line are n positive integers a (a <= 1e5),
representing their values.

输出描述:

For each test case, output an integer, which is the
minimum cost of connecting these cities.

示例1

输入

2

4

1 2 3 4

1

1

输出

12

0

题意: Byteland有n个城市,i城市有价值a。 在两个城市之间建立双向道路的成本是它们的价值的总和。 请计算连接这些城市的最低成本,这意味着任何两个城市都可以相互联系

思路:

贪心算法:想要将任意两个城市连接且道路花费之和最小,那可使每条道路的花费最少,道路的花费等于两端城市value之和,由此可知,只要每个城市与最小value的那个城市相连通,所得的花费必定是最小的。

因此,将最小value的城市放于中间与其他城市一一相连。

AC代码:

#include<iostream>

#include<cstdio>

#include<algorithm>

#include<cstring>

#define p 100001

using namespace std;

int main(){

    int t;

    int a[p];

    scanf("%d",&t);

    while(t--){

        int n;

        scanf("%d",&n);

        for(int i=0;i<n;i++){

            scanf("%d",&a[i]);

        }

         if(n==1){

            printf("0\n");

            continue;

        }

        sort(a,a+n);

        int minn=a[0];

        long long sum=0;

        for(int i=1;i<n;i++){

            sum=sum+a[i]+minn;

        }

        printf("%lld\n",sum);

    }

}



 

D.Dance

链接:https://www.nowcoder.com/acm/contest/123/D
来源:牛客网

题目描述

ALO is a world of magic and elves. The magic can be upgraded and make more power. The elf Leafa occasionally gets a book that records the most powerful magic matrix. Now the wind elves have decided to conjure up the magic matrix, bombing out the most beautiful fireworks to celebrate their spirit festival.

The magic matrix is a combination of magic, which has a strict hierarchy (like a tree). The wind elves can produce the lowest level of magic directly. Low level magic can be upgraded to a higher magic by consuming one specified hand scroll, which will create magic power in the air and become a higher level of magic.

The upgraded magic can still be upgraded to a higher level of magic until it reaches the highest level of magic. Once the magic reach the highest level, the magic matrix will absorb all the magic power in the air. Note magic upgradation is a continuous process, which means you must perform a series of upgradation at a time, in order to make a lowest level of magic into the highest. If you don’t continuously upgraded to the highest level, the magic you made and the magic power the process produced in the air will disappear immediately.

Now the wind elves took all the magic hands scroll out and began to read the magic books that Leafa had found. As the wisest wind elves, the Lord wants you to help build the magic matrix with the most magic power.

输入描述:

The first line includes a integer number n(n ≤ 100000), types of magic (not including the highest magic, and the highest magic is magic 0)

Then there are n line. Each line includes three numbers, and in the i-th line: 

The first integer is prei(0 ≤ prei ≤ n) , that the magic i can be upgraded to the magic prei.

The second integer is numi(numi ≤ 40000), which means wind elves have numi hand scroll to perform such upgradation.

The third integer is poweri(poweri ≤ 1000), which means one such upgradation will produce poweri magic power in the air.

输出描述:

One number, the most magic power.

示例1

输入

7
0 100 0
1 2 3
2 2 5
1 5 1
2 1 3
3 2 4
4 3 2

输出

33

题意:

一个树形结构(根节点是0),给定每个节点的父节点的编号,手轴(hand scroll)个数,从该节点到父节点完成一次升级释放的能量。

升级规则如下:必须从最低层开始,逐层升级,从底层到上一级需消耗一个手轴(hand scroll)才能完成升级,直到升级到最高层,这个时候升级的总能量。

问总能量最大能获得多少?

题解:

模拟样例,样例所对应的树形结构如下

 

其实,题意明确了之后,很容易看到满足要求的路径总共有三条(从叶节点到根),通过这三条路径完成一次升级所释放的能量也是固定的,到底先让哪条先完成升级呢?

显然,每条路径到底能够升级多少次,受手轴(hand scroll)个数的制约。要想让能量最高,那就先让能量高的路径优先完成升级。

代码:

#include<iostream>

#include<cstdio>

#include<vector>

#include<algorithm>

using namespace std;

typedef long long ll;

const int maxn=1e5+10;

int n,fa[maxn],pre[maxn],num[maxn],power[maxn];

vector<int> son[maxn];

int value[maxn],leaf[maxn],cnt;



/*

dfs()时间复杂度分析:

         对于具有n个顶点、e条边的图来说,dfs算法对图中的每个顶点最多调用一次,因此其递归调用总次数为n。当访问某个

顶点v时,dfs的时间主要花在从该顶点出发查找它的邻接点上。当用邻接表表示图时,需要遍历该顶点的所有邻接点,所有dfs

的总时间为O(n+e);当用邻接矩阵表示图时,需要遍历该顶点行的所有元素,所以dfs的总时间为O(n^2).

*/



//对于树来说,e=n-1。用邻接表存储树,所以,dfs时间复杂度为O(n)



void dfs(int id,int w){   //找到叶节点以及每条路径完成每次升级获得到的能量

         value[id]=w;

         if(son[id].size()==0){

                   leaf[cnt++]=id;

                   return ;

         }

         int len=son[id].size();

         for(int i=0;i<len;i++){

                   int x=son[id][i];

                   //printf("power[%d]=%d\n",x,power[x]);

                   dfs(x,w+power[x]);

         }

}



bool cmp(int a,int b){

         return value[a]>value[b];

}



int previs(int x,int w){

    if(x==0) return w;

    if(num[x]==0)   return 0;

    if(num[x]<w){w=num[x];num[x]=0;}

    else num[x]-=w;

    return previs(fa[x],w);

}



int main(){

         scanf("%d",&n);

         for(int i=1;i<=n;i++){

                   scanf("%d%d%d",&fa[i],&num[i],&power[i]);

                   son[fa[i]].push_back(i);

         }

        

         dfs(0,0);

        

         /*for(int i=0;i<cnt;i++){

                   printf("%d %d\n",leaf[i],value[leaf[i]]);

         }*/

         sort(leaf,leaf+cnt,cmp);

        

         ll ans=0;

         int min_num;

         for(int i=0;i<cnt;i++){  //cnt条路径,由路径value[]从大到小进行求能量值

                   int x=leaf[i];

                   min_num=previs(x,num[x]); //注意:这里求每段路径的最小手轴数用递归来求,递推的话会超时

                   ans+=1ll*min_num*value[x];

         }

        

         /*

         //超时

         for(int i=0;i<cnt;i++){  //cnt条路径,由路径value[]从大到小进行求能量值

                   int x=leaf[i];

                   min_num=num[x];

                   int y=fa[x];

                   while(y){

                            min_num=min(min_num,num[y]);

                            y=fa[y];

                   }

                   ans+=1ll*min_num*value[x];

                  

                   if(min_num){

                            y=x;

                            while(y){

                                     num[y]-=min_num;

                                     y=fa[y];

                            }

                   }

         }

        

         */

        

         printf("%lld\n",ans);

         return 0;

}

 

E. Sequence

题目描述

We define an element ai in a sequence "good", if and only if there exists aj (1≤j<i) such that aj<ai.

Given a permutation p of integers from 1 to n. Remove an element from the permutation such that the number of "good" elements is maximized.

输入描述:

The input consists of several test cases. The first

line of the input gives the number of test cases,T(1<=T<=10^3).

For each test case, the first line contains an

integer n(1<=n<=10^6),

representing the length of the given permutation.

The second line contains n integersp1,p2,…,pn (1<=pi<=n),

representing the given permutation p.

It's guaranteed that.

输出描述:

For each test case, output one integer in a single

line, representing the element that should be deleted. If there are several

answers, output the minimal one 。

示例1

输入

2
1
1
5
5 1 2 3 4

输出

1

5

解析:

根据题目描述:存在j< ia[j]< a[i],首先存在一个good序列,让我们从1n个元素中,移除一个元素,使得good数最大化。good数的定义就是题目中的定义:j< ia[j]< a[i],只要满足这样一个条件,就存在good数。为了判断删除序列中的哪一个元素使得good数最大化,就要对每一个数字做判断,用cnt[x]函数代表,删除x后,会减少的good数,找出使得减少的good数最少的元素,删除即可。如果存在多个相同对的good数,就删除最小的那个good数。

代码:

#include<stdio.h>

#include<stdlib.h>

#include<iostream>

#include<algorithm>

using namespace std;

const int maxn=1000005;

int cnt[maxn];

int main()

{

         int t;

         while(scanf("%d",&t)!=EOF)

         {

           while(t--)

           {

              int n;

                    scanf("%d",&n);

                    int min1=maxn,min2=maxn;

                   for(int i=0;i<n;i++)

                   {

                            int x;

                            scanf("%d",&x);

                            cnt[x]=0;

                            if(min1<x && x<min2)

                            {

//此时min1<x<min2, min1的存在使得x成为good数,删除min1,x就不是good数

//所以删除min1之后,good数会减1

                                     cnt[min1]++;

                            }

                            if(min1<x)

                            {

//min1<x,x本身就是一个good数,删除x本身之后,good数就会减1

                                     cnt[x]++;

                            }

                            if(x<min1)

                            {

//此时更新min1与min2的值,使得min1与min2的值为x元素之前的最小值与次最小值

                                     min2=min1;

                                     min1=x;

                            }

                            else

                            {

                                     if(x<min2)

                                     {

//更新min2的值,使得min2的更新为x元素之前的次最小值

                                               min2=x;

                                     }

                            }

                   }

                    int mini=maxn,ans;

                    for(int i=1;i<=n;i++)

                    {

                           if(cnt[i]==mini)//如果有多个good数,那么就选最小的那一个

                           {

                              ans=min(ans,i);

                            }

                           else if(cnt[i]<mini)

                              mini=cnt[i],ans=i;//找使得good数减少的最少的数,这个数就是要移除对的数

                    }

                    printf("%d\n",ans);

           }  

         }

         return 0;

}

F. Four—tuples

题目描述

Given l1,r1,l2,r2,l3,r3,l4,r4, please count the number of four-tuples (x1,x2,x3,x4) such that li≤ xi≤ ri and x1≠x2,x2≠x3,x3≠x4,x4≠x1. The answer should modulo 10^9+7 before output.

输入描述:

The input consists of several test cases. The first

line gives the number of test cases,T(1≤T≤106).

For each test case, the input contains one line

with 8 integers l1,r1,l2, r2, l3,r3,l4,r4(1≤ li≤ ri≤ 10^9)。

输出描述:

For each test case, output one line containing one

integer, representing the answer.

示例1

输入

1

1 1 2 2 3 3 4 4

输出

1

解析:

题意:给四个区间,要求每个区间选一个数字组成一个四元组(x1,x2,x3,x4),要求是x1!=x2,x2!=x3,x3!=x4,x4!=x1。

每一个区间的长度代表区间中数字的个数,从每一个区间中取出一个数字组成一个四元组,就像数学中的组合一样,组合的总数就是四个区间长度的乘积,但是题目中明确了不合法的条件,所以用总数再减去x1==x2,x2==x3,x3==x4,x4==x1这四种两两相交的情况,再加回所有三个集合相交的部分,再减去所有四个集合相交的部分。

四个集合的容斥定理:

 |A∪B∪C∪D|=|A|+|B|+|C|+|D|-|A∩B|-|A∩C|-|A∩D|- |B∩C| - |B∩D| - |C∩D|+|A∩B∩C|+|A∩B∩D|+|A∩C∩D|+|B∩C∩D| -|A∩B∩C∩D|

代码:

#include <stdio.h>

#include <stdlib.h>

#include <iostream>

#include <algorithm>

#include <iostream>

#include <string.h>

using namespace std;

typedef long long int ll;

ll mod=1e9+7;

int main()

{

         int t;

         scanf("%d",&t);

         while(t--)

         {

                   ll l1,r1,l2,r2,l3,r3,l4,r4;

                   ll left, right,accl,accr;

                   scanf("%lld%lld%lld%lld%lld%lld%lld%lld",&l1,&r1,&l2,&r2,&l3,&r3,&l4,&r4);

                   ll ans=(r1-l1+1)%mod*(r2-l2+1)%mod*(r3-l3+1)%mod*(r4-l4+1)%mod;

                   //x1==x2

                   left=max(l1,l2);

                   right=min(r1,r2);

                   if(left<=right)

                     ans=((ans-(right-left+1)*(r3-l3+1)%mod*(r4-l4+1)%mod)%mod+mod)%mod;

                    

                   //x2==x3

                   left=max(l2,l3);

                   right=min(r2,r3);

                   if(left<=right)

                     ans=((ans-(r1-l1+1)*(right-left+1)%mod*(r4-l4+1)%mod)%mod+mod)%mod;

                    

                   //x3==x4

                   left=max(l3,l4);

                   right=min(r3,r4);

                   if(left<=right)

                     ans=((ans-(r1-l1+1)*(r2-l2+1)%mod*(right-left+1)%mod)%mod+mod)%mod;

                    

                   //x4==x1

                   left=max(l1,l4);

                   right=min(r1,r4);

                   if(left<=right)

                     ans=((ans-(right-left+1)*(r2-l2+1)%mod*(r3-l3+1)%mod)%mod+mod)%mod;

                  

                   //x1==x2 && x2==x3

                   left=max(l1,max(l2,l3));

                   right=min(r1,min(r2,r3));

                   if(left<=right)

                     ans=(ans+(right-left+1)*(r4-l4+1)%mod)%mod;

                  

                   //x1==x2 && x2==x4

                   left=max(l1,max(l2,l4));

                   right=min(r1,min(r2,r4));

                   if(left<=right)

               ans=(ans+(right-left+1)*(r3-l3+1)%mod)%mod;

              

             //x1==x2 && x3==x4

             left=max(l1,l2);

             right=min(r1,r2);

             accl=max(l3,l4);

             accr=min(r3,r4);

             if(left<=right && accl<=accr)

               ans=(ans+(right-left+1)*(accr-accl+1)%mod)%mod;

              

             //x2==x3 && x3==x4

             left=max(l2,max(l3,l4));

             right=min(r2,min(r3,r4));

             if(left<=right)

               ans=(ans+(r1-l1+1)*(right-left+1)%mod)%mod;

              

             //x2==x3 && x1==x4

             left=max(l2,l3);

             right=min(r2,r3);

             accl=max(l1,l4);

             accr=min(r1,r4);

             if(left<=right && accl<=accr)

               ans=(ans+(right-left+1)*(accr-accl+1)%mod)%mod;

            

             //x3==x4 && x1==x4

             left=max(l1,max(l3,l4));

             right=min(r1,min(r3,r4));

             if(left<=right)

               ans=(ans+(right-left+1)*(r2-l2+1)%mod)%mod;

              

             //x1==x2 && x2==x3 && x3==x4

             left=max(max(l1,l2),max(l3,l4));

             right=min(min(r1,r2),min(r3,r4));

             if(left<=right)

               ans=((ans-(right-left+1)*3)%mod+mod)%mod; //注意减去的是3倍的x1==x2 && x2==x3 && x3==x4。

              

             printf("%lld\n",ans);

         }

         return 0;

}

G.Games

链接:https://www.nowcoder.com/acm/contest/123/G

来源:牛客网

题目描述

Alice and Bob are playing a stone game. There are n piles of stones. In each turn, a player can remove some stones from a pile (the number must be positive and not greater than the number of remaining stones in the pile). One player wins if he or she remove the last stone and all piles are empty. Alice plays first.

To make this game even more interesting, they add a new rule: Bob can choose some piles and remove entire of them before the game starts. The number of removed piles is a nonnegative integer, and not greater than a given number d. Note d can be greater than n, and in that case you can remove all of the piles.

Let ans denote the different ways of removing piles such that Bob are able to win the game if both of the players play optimally. Bob wants you to calculate the remainder of ans divided by109+7.

输入描述:

The first line contains an integer T, representing

the number of test cases.

For each test cases, the first line are two

integers n and d, which are described above.

The second line are n positive integersai,

representing the number of stones in each pile.

输出描述:

For each test case, output one integer (modulo109+7.) in a

single line, representing the number of different ways of removing piles that

Bob can ensure his victory.

示例1

输入

2

5 2

1 1 2 3 4

6 3

1 2 4 7 1 2

输出

2

5

1.题目含义:

       Alice和Bob在玩游戏,有n堆石头,在一些石块(数量必须是正数,并且不能大于堆中剩余石块的数量)。谁移除最后一块石头并且所有堆都空了,则他获胜(谁面临空的局面,谁将输掉本轮游戏)。Alice是先手。

    但是在游戏开始之前,Bob可以选择一些石头堆并将其全部移除,可以去除的石头堆的数量是非负整数,并且不大于给定的数字d(d>n也是可以的)。在这种情况下,Bob可以去除所有的堆,求解可以使得Bob赢得游戏的方案。

2.分析:

   可以看出,这属于尼姆博弈。尼姆博弈介绍,(可以参考这篇博客学习: http://www.cnblogs.com/jiangjun/archive/2012/11/01/2749937.html)。由此我们可以得出结论:当亦或起来结果为0的时候,先手面临奇异局面,后手取得胜利。

  在本题中,多了附加条件:后手也就是Bob,可以在开始之前移除d堆中所有或者部分石子,以达到自己获胜的目的。移除d堆里面的石子,那么这d堆该如何选择?毋庸置疑,我们需要枚举所有情况,考虑动态规划求解。

3.求解过程:

  3.1 首先我们定义三维dp[1002][12][1030]数组,该三维数组dp[i][j][k]表示:前i堆里面移除j堆,亦或起来结果为k的方案数。

  3.2 状态转化方程:

      动态规划最重要的就是状态转化方程的推导,对于dp[i][j][k]我们可以第i堆属不属于要去除的j堆里面的一堆。故前i堆去j堆的异或值为k的方案数dp[i][j][k]状态有两种来源:

      3.2.1 第i堆石子属于j堆中的一堆:其方案数等于前i-1堆去掉j-1堆的异或值为k的方案数dp[i-1][j-1][k](去掉的不计异或和)

      3.2.2第i堆石子不属于j堆中的一堆;则其方案数等于前i-1堆去掉j堆的异或值为k^a[i]的方案数dp[i-1][j][k^a[i]](留下的计异或和)

4.注意:

    4.1 dp[i][j][k]数组中,k值的大小:k表示亦或的结果,它是a[i]亦或的结果,而a[i]的大小最大是不会超过1002的。1002 的二进制为:111110010,亦或起来也不会超过111111111(1023)。

   4.2数组初始化注意。

5.代码:

#include<iostream>

#include<cstdio>

#include<cstring>

#include<algorithm>

using namespace std;

const int mod = 1e9+7;

int dp[1002][12][1030];//a[i]<=1002,亦或起来的值一定是小于1024(2^10)

int a[1002];

int main()

{

    int t;

    int n,d;

    scanf("%d", &t);

    while (t--)

    {

        scanf("%d%d", &n,&d);

        for (int i = 1; i <= n; i++)

            scanf("%d", &a[i]);

        memset(dp, 0, sizeof(dp));

        dp[1][1][0] = 1;

        dp[1][0][a[1]] = 1;

        for (int i = 2; i <= n; i++)

        {

            int num = min(i, d);

            for (int j = 0; j <= num; j++)

            {

                for (int k = 0; k < 1024; k++)

                {

                    dp[i][j][k] = (dp[i-1][j-1][k] + dp[i-1][j][k^a[i]]) % mod;

//i堆里面去掉j堆,就可以有两种情况,第i堆属于j堆里面的一个,i堆不属于里面的一个

                }

            }

        }

        int ans = 0;

        for (int i = 0; i <= min(d,n); i++)

        {

             ans=(ans+dp[n][i][0])%mod;

//只要是异或起来起来为0,那么就是Bob赢的一种情况

             //cout<<"i="<<i<<",dp[n][i][0]="<<dp[n][i][0]<<",ans="<<ans<<endl;

              }

        printf("%d\n", ans);

    }

    return 0;

}

H.Dominoes

链接:https://www.nowcoder.com/acm/contest/123/H

来源:牛客网

题目描述

Orz likes to play dominoes. Now giving an n∗m chessboard and k dominoes whose size are 1∗2, Orz finds that there is exactly one grid empty, so that he can move dominoes without overlapping them. An initial situation is given, he wants to know how many final situation could be achieved, except the initial situation. Note every domino is different, as they have their own serial number. Since the answer may be very large, please output the answer modulo 1000000009.

输入描述:

There will be multiple test cases. For each test

case:

The first line contains three integers: n,m,k(n≤9,m≤10000).

The following k lines, each line contains four

integers: a b c d, indicating that this domino occupies (a, b) and (c, d).

The input guarantees that the domino does not

overlap, and there is exactly one empty grid.

输出描述:

For each test cases, output the answer modulo

1000000009.

示例1

输入

5 5 12

1 1 2 1

1 2 2 2

1 3 2 3

1 4 1 5

2 4 2 5

3 4 3 5

3 1 3 2

4 1 4 2

5 1 5 2

4 3 5 3

4 4 5 4

4 5 5 5

输出

8

1.题意:

   现在给出一个n*m棋盘和k个多米诺骨牌,其大小为1*2,且只有一个网格是空的,这样他就可以移动多米诺骨牌而不重叠它们。求有多少最终情况可以实现,除了最初的情况。每个多米诺骨牌都是不同的,因为他们有自己的序列号。输出答案取余1000000009。

2.思路:

   按空格位置(右上角顶点)进行搜索,检查状态合不合格就好:

 

如上图所示的题目样例, 5*5的格子,12张诺骨牌,最开始标记空格的位置是(3,3),然后进行广搜,八个位置如下:

 

特别注意有一个要判断的就是,属不属于一个多骨诺牌:设空格现在位于(3,3)移动后到达(1,3) 。现在搜索的时候让它往下面移动,就会到达(1,1)这个位置,由上面的图片我们知道,这显然是不合适的,因为(1,1)和(1,2)属于不同的颜色。

3.代码:

#include<bits/stdc++.h>

using namespace std;

typedef long long LL;

const LL mod = 1e9+7;

int n,m,k;

int mp[10][10010];

int vis[10][10010];

int dir[4][2]={-2,0,2,0,0,-2,0,2};

struct node

{

    int x,y;

};



int judge( int x , int y )

{

    if ( x<1||x>n||y<1||y>m )

        return 0;

    else

        return 1;

}

int bfs( int sx , int sy )

{

    memset (vis,0,sizeof(vis));

    node p,q;

    p.x = sx;

    p.y = sy;

    queue<node>Q;

    Q.push(p);

    vis[p.x][p.y] = 1;

    while ( !Q.empty() )

    {

          //int dir[4][2]={-2,0,2,0,0,-2,0,2};

          //                 左  右   下   上

        p = Q.front();

        Q.pop();

        for ( int i=0 ; i<4 ; i++ )

        {

            q.x = p.x + dir[i][0];

            q.y = p.y + dir[i][1];

            if ( !judge(q.x,q.y)||vis[q.x][q.y] )//越出边界就继续

                         continue;

            int fx=p.x+dir[i][0]/2;

                     int fy=p.y+dir[i][1]/2;

                     //cout<<"q.x="<<q.x<<",q.y="<<q.y<<",fx="<<fx<<",fy="<<fy<<endl;

            if ( mp[q.x][q.y]!=mp[fx][fy] )

//移动完成后不相等,就说明并不符合要求,比如(1,1,)和(1,2)位置就属于两个颜色

                        continue;

                     //cout<<"q.x="<<q.x<<",q.y="<<q.y<<endl;

                     //cout<<"**************"<<endl;

            vis[q.x][q.y] = 1;

            Q.push(q);

           

        }

    }

    int ans = 0;

    for ( int i=1 ; i<=n ; i++ )

        for ( int j=1 ; j<=m ; j++ )

            if ( vis[i][j] )

                        ans++;

       ans--;//减去第一个空格点的

       return ans;

}



int main()

{

    while(scanf("%d%d%d",&n,&m,&k)!=EOF)

    {

        memset ( mp , 0 , sizeof(mp) );

        for ( int i=1;i<=k;i++ )

        {

            int a,b,c,d;

            scanf ( "%d%d%d%d",&a,&b,&c,&d);

            mp[a][b] = i;

            mp[c][d] = i;

        }

        int sx,sy;

        for ( int i=1 ;i<=n;i++ )

            for ( int j=1;j<=m;j++ )

                if (mp[i][j]==0 )//找到留出来的是空格的地方

                    {

                      sx=i;

                      sy=j;

                                   }

        printf ("%d\n",bfs(sx,sy)%mod );

    }

    return 0;

}

 

 

 

 

 

猜你喜欢

转载自blog.csdn.net/LMengi000/article/details/83036526