第七届山东省ACM省赛---problem I. Rock Paper Scissors(状压统计)

problem I. Rock Paper Scissors

题目大意:

N个人剪刀包袱锤,每人和剩下的人(N-1)比赛,每场比赛出10次

输出每个人赢分别0,1,2……10次的次数

规则:

P>R   S>P   R>S

e.g.

first RRSSSPPPPP

second RRRRRRRRRR

第一个人赢5次,第二个人赢3次

对应输出

00000100000

00010000000

Sample

Input

2
4
RRRRRRRRRR
SSSSSSSSSS
PPPPPPPPPP
RRSSSPPPPP
6
RRRRPPPRPP
PSRSPPPPSP
RSPPRSSRRP
SPPRSRRRSS
RPPSPRSSSS
RSRRSPRSSP

Output

Case #1:
1 0 0 1 0 0 0 0 0 0 1
1 0 0 0 0 1 0 0 0 0 1
1 0 1 0 0 0 0 0 0 0 1
0 0 1 1 0 1 0 0 0 0 0
Case #2:
0 0 1 4 0 0 0 0 0 0 0
0 0 1 2 2 0 0 0 0 0 0
0 0 0 1 2 1 1 0 0 0 0
0 0 0 2 1 2 0 0 0 0 0
0 1 1 0 2 1 0 0 0 0 0
0 1 1 2 0 1 0 0 0 0 0

 

分析:

第一想到暴力

Time Limit Exceeded

太傻太天真

#include "cstdio"
#include "cstring"
#include "iostream"
using namespace std;
#define N 30010
char player[N][10];
int ans[11];
void solve(int n)
{
    int cnt=0;
    for(int i=0;i<n;i++)
    {
        memset(ans,0,sizeof(ans));
        for(int k=0;k<n;k++)
        {
            if(i==k)continue;
            cnt=0;
           for(int j=0;j<10;j++)
            {
                if(player[i][j]=='S'&&player[k][j]=='P')
                {
                    cnt++;
                    continue;
                }
                else if(player[i][j]=='P'&&player[k][j]=='R')
                {
                    cnt++;
                    continue;
                }
                else if(player[i][j]=='R'&&player[k][j]=='S')
                {
                    cnt++;
                }
            }
            ans[cnt]++;
            ///cout<<"----------------------"<<cnt<<endl;
        }
        printf("%d",ans[0]);
        for(int i=1;i<=10;i++)
            printf(" %d",ans[i]);
        printf("\n");
    }
}

int main()
{
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    int t,n,cnt=0;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%s",player[i]);
        printf("Case #%d:\n",++cnt);
        solve(n);
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}

上面解法太多重复计算的地方,仅就两两比较次数(n-1)^n

下面优化算法,避免重复计算,比较次数,c(n,2)=n*(n-1)/2;

依旧TLE

太傻太天真+1

#include "cstdio"
#include "cstring"
#include "iostream"
using namespace std;
#define N 30010
char player[N][10];
int ans[N][11];void solve(int n)
{
    for(int i=0;i<n;i++)
        memset(ans[i],0,sizeof(ans[i]));
    int cnt1=0,cnt2=0;
    for(int i=0;i<n;i++)
    {
        for(int k=i+1;k<n;k++)
        {
            if(i==k)continue;
            cnt1=0;cnt2=0;
           for(int j=0;j<10;j++)
            {
                if(player[i][j]=='S'&&player[k][j]=='P')
                {
                    cnt1++;
                    continue;
                }
                else if(player[i][j]=='P'&&player[k][j]=='S')
                {
                    cnt2++;
                    continue;
                }
                else if(player[i][j]=='P'&&player[k][j]=='R')
                {
                    cnt1++;
                    continue;
                }
                else if(player[i][j]=='R'&&player[k][j]=='P')
                {
                    cnt2++;
                    continue;
                }
                else if(player[i][j]=='R'&&player[k][j]=='S')
                {
                    cnt1++;
                }
                else if(player[i][j]=='S'&&player[k][j]=='R')
                {
                    cnt2++;
                    continue;
                }
            }
            ans[i][cnt1]++;
            ans[k][cnt2]++;
            ///cout<<"----------------------"<<cnt<<endl;
        }
    }
    for(int i=0;i<n;i++)
    {
        printf("%d",ans[i][0]);
        for(int j=1;j<=10;j++)
            printf(" %d",ans[i][j]);
        printf("\n");
    }
}

int main()
{
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    int t,n,cnt=0;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%s",player[i]);
        printf("Case #%d:\n",++cnt);
        solve(n);
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}

榜单

事情并没有你想的那么简单

感谢http://www.cnblogs.com/shuguangzw/p/5585715.html

状压统计  

就是改成把一个字符串改成三进制状压,然后分成前5位,后5位统计,

然后直接统计 f[i][j][k]代表,后5局状压为k的,前5局比和j状态比输了5局的有多少个人

复杂度是O(T*30000*25*m)m比较小,也就最多几十吧,将将过

 

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <vector>
#include <cmath>
#include <queue>
#include <map>
#include <string>
using namespace std;
typedef long long LL;
const int N=3e4+5;
const int M=243;
const int INF=0x3f3f3f3f;
int get(int x,int y){
  int ret=0;
  for(int k=0;k<5;++k){
    int dig0=x%3,dig1=y%3;
    if(dig0==(dig1+1)%3)++ret;
    x/=3,y/=3;
  }
  return ret;
}
vector<int> win[6][M],lose[6][M];
int T,cas,n,a[N][11],ret[N][11],f[6][M][M];
char ch[15];
int main(){
   for(int i=0;i<M;++i){
    for(int j=0;j<M;++j){
      win[get(i,j)][i].push_back(j);
      lose[get(i,j)][j].push_back(i);
    }
   }
   scanf("%d",&T);
   while(T--)
   {
    scanf("%d",&n);
    for(int i=0;i<n;++i)
    {
      scanf("%s",ch);
      for(int j=0;j<10;++j)
      {
        if(ch[j]=='R')a[i][j]=0;
        else if(ch[j]=='P')a[i][j]=1;
        else a[i][j]=2;
      }
    }
    memset(f,0,sizeof(f));
    memset(ret,0,sizeof(ret));
    for(int i=0;i<n;++i)
    {
       int mask0=0,mask1=0;
       for(int j=0;j<5;++j)mask0=mask0*3+a[i][j];
       for(int j=5;j<10;++j)mask1=mask1*3+a[i][j];
       for(int j=0;j<=5;++j)
       {
          for(int k=0;k<lose[j][mask0].size();++k)
          {
              int t=lose[j][mask0][k];
              ++f[j][t][mask1];
          }
       }
    }
    for(int i=0;i<n;++i){
       int mask0=0,mask1=0;
       for(int j=0;j<5;++j)mask0=mask0*3+a[i][j];
       for(int j=5;j<10;++j)mask1=mask1*3+a[i][j];
       for(int s0=0;s0<=5;++s0)
         for(int s1=0;s1<=5;++s1){
           for(int k=0;k<win[s1][mask1].size();++k){
             int t=win[s1][mask1][k];
             ret[i][s0+s1]+=f[s0][mask0][t];
           }
         }
      --ret[i][0];
    }
    printf("Case #%d:\n",++cas);
    for(int i=0;i<n;++i){
      for(int j=0;j<10;++j)
        printf("%d ",ret[i][j]);
      printf("%d\n",ret[i][10]);
    }
   }
   return 0;
}

 

posted @ 2017-03-31 17:11  kimsimple  阅读(250)  评论(0)    收藏  举报