1. 程式人生 > >“浪潮杯”山東省第九屆ACM大學生程式設計競賽

“浪潮杯”山東省第九屆ACM大學生程式設計競賽

目錄

題目描述

輸入描述:

輸出描述:

輸入

輸出

解析:

程式碼:

題目描述

輸入描述:

輸出描述:

輸入

輸出

題目描述

輸入描述:

輸出描述:

輸入

輸出

題目描述

輸入描述:

輸出描述:

輸入

輸出

題意:

題解:

程式碼:

題目描述

輸入描述:

輸出描述:

輸入

輸出

解析:

程式碼:

題目描述

輸入描述:

輸出描述:

輸入

輸出

解析:

程式碼:

題目描述

輸入描述:

輸出描述:

輸入

輸出

2.分析:

4.注意:

5.程式碼:

題目描述

輸入描述:

輸出描述:

輸入

輸出

1.題意:

2.思路:

3.程式碼:

題目描述

  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;

}

題目描述

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 109

輸出描述:

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程式碼:
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
const int maxn=550;
int n;
int from[maxn],w[maxn][maxn],ans,vis[maxn];
vector<int> g[maxn];
bool Find(int u)
{
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(!vis[v])
        {
            vis[v]=1;
            if(from[v]==-1 || Find(from[v]))
            {
                from[v]=u;
                return true;
            }
        }
    }
    return false;
}
int match()//求出最大能射死怪獸的數量
{
    int ret=0;
    memset(from,-1,sizeof(from));
    for(int i=0;i<n;i++)
    {
        memset(vis,0,sizeof(vis));
        if(Find(i)  ) ret++;
    }
    return ret;
}
bool 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))
    {
        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();
        int L=0,R=1e9;
        while(L<R)
        {
            int mid=(L+R+1)>>1;
            if(check(mid))
                L=mid;
            else
                R=mid-1;
        }
        printf("%d\n",L);
    }
    return 0;
}

題目描述

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);

    }

}



題目描述

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;

}

題目描述

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;

}

題目描述

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;

}

連結: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

       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.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;

}

連結: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++;