2016年山东省acm比赛题解(差l和h)

Posted 罚时自动机

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2016年山东省acm比赛题解(差l和h)相关的知识,希望对你有一定的参考价值。

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\\n",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\\n",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\\n",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\\n",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\\n",ans);
    }
    return 0;
}
View Code

h.Memory Leak

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

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

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

#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()] = \'\\0\';
            }
            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

以上是关于2016年山东省acm比赛题解(差l和h)的主要内容,如果未能解决你的问题,请参考以下文章

牛客2021年愚人节比赛 题解

2017年广东省ACM省赛(GDCPC-2017)总结

第十一届山东省大学生程序设计竞赛题解(9 / 13)

ACM2016级新生第三周训练赛

第十届山东省ACM省赛题解

2021年山东省C++应用编程比赛小学组参考代码及视频讲解