2016年山东省acm比赛题解完全版

a Julyed

题意:n个单词,每天最多背m个单词,最少背多少天

分析:水题,不解释

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

int main(){
    int t,n,m;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        int ans=n/m;
        if(n%m!=0)
          ans++;
        printf("%d
",ans);
    }
    return 0;
}
View Code

b. Fibonacci

题意:把n分解为Fibonacci数列的和,并且两个数不能连续

分析:其实两个数不能连续没有用,证明一下如果n=f1+f2+x,分类讨论下,x=f2,n那么n=2*f1+f2=f2+f3=f4,如果x=f3,那么n=f1+f2+f3=f1+f4,如果x==f4  n=f1+f2+f4=f3+f4=f5,如果n>f4

n=f3+x

所以n从大到小依次减Fibonacci,然后倒序输出就ok了

#include<bits/stdc++.h>
using namespace std;
const int maxn=105;
int d[maxn],sz;

void init(){
    d[0]=d[1]=1;
    for(sz=2;;sz++){
        d[sz]=d[sz-1]+d[sz-2];
        if(d[sz]>=1e9)
          break;
    }
}

int main(){
    init();
    int t,n;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        stack<int> s;
        int tmp=n;
        for(int i=sz;i>0;i--)
            if(d[i]<=n){
                s.push(d[i]);
                n-=d[i];
            }
            printf("%d=",tmp);
            int a=s.top();s.pop();
            printf("%d",a);
            while(!s.empty()){
                a=s.top();s.pop();
                printf("+%d",a);
            }
            puts("");

    }
    return 0;
}
View Code

c.Proxy

题意:0是本地计算机,n+1是服务器,1-n是代理服务器,给出每条线路的时间消耗,然后找到最短的0到n+1的路径,然后输出0处选择的第一个代理服务器,如如果不存在,输出-1,如果0-n+1直连且最短,输出0

分析:mdzz,读错题了,哎,说多了都是泪,都出的水题我们没出,dijkstra,把边倒着输入,然后找n+1-0的最短路,松弛操作记录前驱,特殊情况判断下就ok了

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int INF=1e9;

struct Edge{
    int from,to,dist;
    Edge(int u,int v,int w):from(u),to(v),dist(w){}
};

struct HeapNode{
    int d,u;
    bool operator < (const HeapNode& r) const{
        return d>r.d;
    }
};

struct Dijkstra{
    int n,m;
    vector<Edge> edges;
    vector<int> G[maxn];
    bool done[maxn];
    int d[maxn];
    int p[maxn];

    void init(int n){
        this->n=n;
        for(int i=0;i<n;i++)
            G[i].clear();
        edges.clear();
    }

    void AddEdges(int from,int to,int dist){
        edges.push_back(Edge(from,to,dist));
        m=edges.size();
        G[from].push_back(m-1);
    }

    void dijkstra(int s){
        priority_queue<HeapNode> q;
        for(int i=0;i<n;i++)
            d[i]=INF;
        d[s]=0;
        memset(done,0,sizeof(done));
        q.push((HeapNode){0,s});
        while(!q.empty()){
            HeapNode x=q.top();q.pop();
            int u=x.u;
            if(done[u]) continue;
            done[u]=true;
            for(int i=0;i<G[u].size();i++){
                Edge& e=edges[G[u][i]];
                if(d[e.to]>d[u]+e.dist){
                    d[e.to]=d[u]+e.dist;
                    p[e.to]=u;
                    q.push((HeapNode){d[e.to],e.to});
                }
                else if(d[e.to]==d[u]+e.dist)
                  p[e.to]=min(p[e.to],G[u][i]);
            }
        }
    }
}dijkstra;

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        scanf("%d%d",&n,&m);
        dijkstra.init(n+2);
        int u,v,w;
        while(m--){
            scanf("%d%d%d",&u,&v,&w);
            dijkstra.AddEdges(v,u,w);
        }
        dijkstra.dijkstra(n+1);
        if(dijkstra.d[0]>=INF){
            puts("-1");
            continue;
        }
        if(dijkstra.p[0]==n+1){
            puts("0");
            continue;
        }
        printf("%d
",dijkstra.p[0]);
    }
    return 0;
}
View Code

d.Swiss-system tournament

2*n个人,每个人有一个初始的分数,和能力值,按照分数排序,分数相同,序号小的在前面,然后每次(1 2)(3 4),这样的两个人比较,能力值大的+1分,小的不变,最后输出排名为k的人的编号

分析:zz题,当时没几个出的,思路题,想不出来没办法,要注意到失败的人是有序,胜利的人是有序的,+1后依然是有序的,两个数组分别存,这不就是归并排序的定义嘛,O(n)时间完成排序,总时间复杂度O(n*logn+r*n)

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int n,m,r;
struct node{
    int id,x,s;
    void read(int a,int b,int c){
        id=a;
        x=b;
        s=c;
    }
}v[maxn*2],v1[maxn],v2[maxn];

bool cmp(node a,node b){
    if(a.x==b.x)
      return a.id<b.id;
    return a.x>b.x;
}

void solve(){
    int l=0,r=0;
    for(int i=0;i<n;i+=2){
        int a=i,b=i+1;
        if(v[a].s<v[b].s||(v[a].s==v[b].s&&v[a].id>v[b].id))
          swap(a,b);
        v1[l++].read(v[a].id,v[a].x+1,v[a].s);
        v2[r++].read(v[b].id,v[b].x,v[b].s);
    }
    int a=0,b=0,c=0;
    while(a<l||b<r){
        if((b>=r)||(a<l&&(v1[a].x>v2[b].x||(v1[a].x==v2[b].x&&v1[a].id<v2[b].id))))
            v[c++]=v1[a++];
        else
          v[c++]=v2[b++];
        //printf("%d ",v[c-1].x);
    }
//    puts("");
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d%d",&n,&r,&m);
        n*=2;
        for(int i=0;i<n;i++){
            scanf("%d",&v[i].x);
            v[i].id=i;
        }
        for(int i=0;i<n;i++)
          scanf("%d",&v[i].s);
        sort(v,v+n,cmp);
        while(r--)
          solve();
        printf("%d
",v[m-1].id+1);
    }
    return 0;
}
View Code

e.The Binding of Isaac

题意:有一些房间,周围都是空地,求只与一个房间相邻的空地数

分析:这个题都出了吧,看到第一眼以为是个搜索,没想到就一水题,n,m周围都搞成空地,然后输入就行,之间判断每个空地周围房间数

#include<bits/stdc++.h>
using namespace std;
const int maxn=105;
const int dx[]={0,0,1,-1};
const int dy[]={1,-1,0,0};
char g[maxn][maxn];
int n,m;
int judge(int x,int y){
    int count=0;
    for(int i=0;i<4;i++){
        int nx=x+dx[i];
        int ny=y+dy[i];
        if(nx<0||nx>n+1||ny<0||ny>m+1)
          continue;
        if(g[nx][ny]=='#')
          count++;
    }
    return count==1?1:0;
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);getchar();
        for(int i=0;i<=n+1;i++)g[i][0]=g[i][m+1]='.';
        for(int i=0;i<=m+1;i++)g[0][i]=g[n+1][i]='.';
        for(int i=1;i<=n;i++){
            scanf("%s",g[i]+1);
            getchar();
        }
        int ans=0;
        for(int i=0;i<=n+1;i++)
          for(int j=0;j<=m+1;j++)
            if(g[i][j]=='#')
              continue;
            else
              ans+=judge(i,j);
        printf("%d
",ans);
    }
    return 0;
}
View Code

f.Feed the monkey

题意:三个东西分别有n1,n2,n3个,每天取一个物品,但是三种物品连续取得不得超过d1,d2,d3个,问有多少取方案

分析:当时一看,算了下转态50^4*3,1e7多点,没问题,时间足够,而且非常多的状态达不到,就采用了记忆化搜索,当时脑残了,有个错就是找不到,遗憾,出来就找到了,悲哀,莫过于赛后过题

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
int dp[55][55][55][55][3];
int n[3],num[3];

int dfs(int a,int b,int c,int d,int e){
    int& z=dp[a][b][c][d][e];
    if(a==0&&b==0&&c==0)return z=1;
    if(z!=-1)
      return z;
    ll ans=0;
    for(int i=0;i<3;i++){
        if(i==0&&a==0)continue;
        else if(i==1&&b==0)continue;
        else if(i==2&&c==0)continue;
        int p;
        if(e==i){
            p=d+1;
            if(p>num[i])continue;
        }
        else
          p=1;
        if(i==0)
            ans+=dfs(a-1,b,c,p,i);
        else if(i==1)
          ans+=dfs(a,b-1,c,p,i);
        else if(i==2)
          ans+=dfs(a,b,c-1,p,i);
        ans%=mod;
    }
    return z=(int)ans;
}


int main(){
    int t;
    cin>>t;
    while(t--){
        for(int i=0;i<3;i++)
          cin>>n[i];
        for(int i=0;i<3;i++)
            cin>>num[i];
        ll ans=0;
        memset(dp,-1,sizeof(dp));
        for(int i=0;i<3;i++){
            if(n[i]==0)
              continue;
            if(i==0)
                ans+=dfs(n[0]-1,n[1],n[2],1,i);
            else if(i==1)
              ans+=dfs(n[0],n[1]-1,n[2],1,i);
            else if(i==2)
              ans+=dfs(n[0],n[1],n[2]-1,1,i);
            ans%=mod;
        }
        cout<<ans<<endl;


    }
    return 0;
}
View Code

g.Triple Nim

题意:nim游戏都玩过吧,把n个数字分成三堆,两人采取最佳策略,先手输

分析:先手输,也就是说三堆异或为0,然后我就往二进制上想,先想到奇数无解,因为最后的那个1,没有其他的1和他去^,然后由此我想到对应位的1只能分解为下一位的两个1,当时没想到组合数,我蒙了一个公式,决定赌一发,wa了,然后我继续考虑n中二进制有4个1的情况,然后想到一个神奇的式子(3^3-1)/2,一试,2 3个1的时候也成立,然后就搞了,a了,赛后想了下原理,比如20,二进制分解为11110,第四位必须放两个1,然后就是剩下三个1的放法了,每个1有三种方法,也就是3^3,减去第三个数什么都没得到的情况,然后(1,3,4)和(1,4,3)是同一种情况,所以/2

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

int main(){
    int t,n;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        if(n&1){
            puts("0");
            continue;
        }
        int count=-1;
        for(int i=1;i<=31;i++)
            if(n&(1<<i))
              count++;
        long long ans=pow(3,count)-1;
        ans/=2;
        printf("%lld
",ans);
    }
    return 0;
}
View Code

h.Memory Leak

题意:顾名思义,内存泄漏吗,题意就是题目图片那个题意,没啥坑,比赛的时候的还以为掉坑了,哎

分析:那就模拟呗,把三个整在一个char 数组里最好搞了,记录输入的三个数组的名字和开始结尾,然后输出的时候,就输出呗,直到遇到'',反正题目给了,最后肯定有''结尾

  当时比赛的时候,就这么写的,测试数据都过,不知道哪写错了

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 11111;
typedef pair<int, int>pii;
char a[MAXN];
string tmp;
string input;
map<string, pii>m;

int main(){
    ios::sync_with_stdio(false);
    int T;
    string cmd, name;
    cin >> T;
    while (T--){
        m.clear();
        cin.ignore();
        memset(a, 0, sizeof(a));
        getline(cin, input);
        istringstream sin(input);
        sin >> tmp;
        int pos = 0;
        while (sin >> tmp){
            int l = tmp.find('['), r = tmp.find(']'), length;
            name = tmp.substr(0, l);
            istringstream sz(tmp.substr(l + 1, r - l + 1));
            sz >> length;
            m[name] = pii(pos, pos + length);
            pos += length;
        }
        while (cin >> cmd >> name && cmd[0] != 'r'){
            pii sb = m[name];
            if (cmd == "gets"){
                cin.ignore();
                getline(cin, input);
                for (int i = 0; i < input.length(); i++)
                    if (i + sb.first < sb.second)
                        a[i + sb.first] = input[i];
                    else
                        break;
                if (sb.second - sb.first > input.length())
                    a[sb.first + input.length()] = '';
            }
            else
            {
                cout << a + sb.first << endl;
            }
        }
    }
    return 0;
}
View Code

iRock Paper Scissors

题意:有n个,玩石头剪刀布游戏,每个人出拳10次,每个人可以和其他的任何人玩一次,输出每个人赢0-10次的次数

分析:教练说,看上去像网络流,网络流解决一切问题,不过,看了测试代码,发现不是网络流,是hash+预处理

我先捋捋思路,一会更新

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 7;
const int Limit = 243; // 3^5

int Win(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][Limit], lose[6][Limit];

void Init() {
    for (int i = 0; i < Limit; i ++) {
        for (int j = 0; j < Limit; j ++) {
            win[Win(i, j)][i].push_back(j);
            lose[Win(i, j)][j].push_back(i);
        }
    }
}

int T, cas, n, a[N][11], res[N][11], f[6][Limit][Limit];
char ch[11];

int main() {
    Init();
    cin >> 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;
                if (ch[j] == 'P') a[i][j] = 1;
                if (ch[j] == 'S') a[i][j] = 2;
            }
        }
        memset(f, 0, sizeof(f));
        memset(res, 0, sizeof(res));
        int tot = 0;
        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];
            int tot = 0;
            for (int j = 0; j <= 5; j ++) {
                for(int libiao = 0;libiao<lose[j][mask0].size();libiao++){
                    int t = lose[j][mask0][libiao];
                    tot ++;
                    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 libiao = 0;libiao < win[s1][mask1].size();libiao++){

                    int t = win[s1][mask1][libiao];
                    res[i][s0 + s1] += f[s0][mask0][t];
                }
            }
            res[i][0] --;
        }
        printf("Case #%d:
", ++ cas);
        for (int i = 0; i < n; i ++) {
            for (int j = 0; j <= 10; j ++) {
                printf("%d%c", res[i][j], j == 10 ? '
' : ' ');
            }
        }
    }
    return 0;
}
View Code

j Execution of Paladin

题意:http://hs.178.com/201606/259352896164.html

分析:分析毛啊,没玩过炉石的伤不起啊,就这样吧,队友写的

#include<cstdio>
#include<queue>
#include<functional>
#include <iostream>
#include <vector>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
string s;
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        getchar();
        int a=0,b=0,c=0;
        for(int i=0;i<n;i++)
        {
            getline(cin,s);
            if(s[0]=='M')
                a++;
            if(s[0]=='B')
                b++;
            if(s[0]=='O')
                c++;
        }
        int cnt=2*(b+c)+2*a*(b+c)+(n-1)*c;
        if(cnt>=m)
            printf("Mrghllghghllghg!
");
        else
            printf("Tell you a joke, the execution of Paladin.
");
    }
}
View Code

k.Reversed Words

题意:不用说了,一看就知道,直接上代码

#include<bits/stdc++.h>
using namespace std;
string s;
int main(){
    int t;
    scanf("%d",&t);getchar();
    while(t--){
        getline(cin,s);
        stack<string> ans;
        string tmp="";
        int len=s.length();
        for(int i=len-1;i>=0;i--)
            if(s[i]!=' ')
              tmp+=s[i];
            else{
                ans.push(tmp);
                tmp="";
            }
        cout<<tmp;
        while(!ans.empty()){
            tmp=ans.top();
            ans.pop();
            cout<<" "<<tmp;
        }
        cout<<endl;
    }
    return 0;
}
View Code

l Password

题意,6种操作,用最少的次数,把第一个数变成第二个数

分析:开始我们都是把注意力集中在了如何把第一个转化为第二个数,只想着记录状态,然后暴力搜索,没有注意到test有1000条,也没有想到把移位操作和加减操作分开,

试了各种千奇百怪的搜索,废话不多说,下面上干货

这个呢就是预处理移位操作,就是原来某位的数字到了其他位置,我位移操作要要走多少步,那么如果用1 2 3 4 5 6表示数字a的6个数的下表,那么总共情况有6!钟,那么数字的位置都确定了,剩下的只要再加上和对应位数字的差就行了,但是还要注意到一点,举个例子 比如状态2 1 3 4 5 6,如果用V[213456表示123456到达这个状态的话,没法表示他对3456这四个位置的修改权利,只有你的下标走到过

L-1,你才对L有修改的权利(思想类黑书钓鱼那道题),这样用6!*6就可以描述所有的移位状态

换位操作,i可以和i-1,i+1进行交换,

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
#define rep(i , n) for(int i = 0 ; i < (int)n ; i++)
#define rep1(i , x, y) for(int i = (int)x; i<=(int)y; i++)

const int inf = 0x3f3f3f3f;
int encode(int* a)
{
    int num = 0 ;
    rep(i, 6)
    num = num * 10 + a[i];
    return num;
}
void decode(int x,int * a)
{
    for(int i = 5 ; i>= 0 ; i--)
    {
        a[i] = x % 10;
        x/=10;
    }
}
const int N = 1e6 + 100;
int vis[N][6][6], VIS[N][6];
struct node
{
    int s, p, max_;
    node(int s = 0,int p = 0, int max_ = 0) :s(s),p(p), max_(max_) {}
};
queue<node> q;
void Push(int s, int p, int max_,int val)
{
    if(vis[s][p][max_] == -1)
    {
        vis[s][p][max_] =  val + 1;
        VIS[s][max_] = min(vis[s][p][max_], VIS[s][max_]);
        q.push(node(s, p ,max_));
    }
}
void init_bfs()
{
    memset(vis,-1,sizeof(vis));
    memset(VIS,inf,sizeof(VIS));
    VIS[123456][0] = 0;
    int a[7];
    rep(i, 6) a[i] = i + 1;
    q.push(node(encode(a), 0, 0));
    vis[encode(a)][0][0] = 0;
    while(!q.empty())
    {
        node u = q.front();
        q.pop();
        int val = vis[u.s][u.p][u.max_];
        if(u.p > 0)
        {
            Push(u.s, u.p - 1, u.max_, val);
            int b[7];
            decode(u.s, b);
            swap(b[u.p], b[u.p - 1]);
            int nts = encode(b);
            Push(nts, u.p, u.max_, val);
        }
        if(u.p < 5)
        {
            Push(u.s, u.p + 1, max(u.max_, u.p + 1), val);
            int b[7];
            decode(u.s, b);
            swap(b[u.p], b[u.p + 1]);
            int nts = encode(b);
            Push(nts, u.p, max(u.max_, u.p + 1), val);
        }
    }
}
int a[7], b[7];
void solve()
{
    int c[6] = {1, 2, 3, 4, 5, 6};
    int ans = inf;
    do
    {
        int all = 0, max_ = 0 ;
        rep(i, 6)
        {
            int fro = a[c[i] - 1];
            int to_ = b[i];
            if(fro != to_) max_ = max(max_, c[i] - 1 );
            all += abs(fro - to_);
        }
        int exc = inf;
        for(int i = max_ ; i< 6 ; i++)
            exc = min(exc, VIS[encode(c)][i]);
        all += exc;
//        if(encode(c) == 612345 ){
//              cout<<all <<" "<< exc<<" "<< max_ << endl;
//        }
        ans = min(ans,all);
    }
    while(next_permutation(c, c + 6));
    printf("%d
",ans);
}
char s[100];
void read(int* a)
{
    scanf("%s",s);
    rep(i, 6){
        a[i] = s[i] - '0';
    }
}
int main()
{
    init_bfs();
    int T;
    scanf("%d",&T);
    while(T--)
    {
        read(a);
        read(b);
        solve();
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/jihe/p/5573378.html