ACM-ICPC 2017 Asia Nanning

  • Post author:
  • Post category:其他




A. Abiyoyo、



题意

输出n个

Abiyoyo, Abiyoyo.

在输出两个

Abiyoyo, yo yoyo yo yoyo.



代码

#include<iostream>

using namespace std;

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n;
        scanf("%d",&n);
        while(n--){
            puts("Abiyoyo, Abiyoyo.");
        }
        puts("Abiyoyo, yo yoyo yo yoyo.");
        puts("Abiyoyo, yo yoyo yo yoyo.");
    }
    return 0;
}



E The Champion



题意

给了r,k,p 三个数,表示一用有 2

r

给人,当前这个人在第k个强,如果强的人和弱的人打获胜的概率为p,求这个人获胜的最大可能



思路

如果一个要想获得胜率的可能性最大,那么他应该尽可能先和比他弱的人打(先和获胜概率大的人比赛,否则这个概率会一直在比赛中间接性的减小),争取强的人淘汰更多强的人,按照这个贪心的思路,如果有弱的人,那么就和弱的人打,就只能和强的人打,那么弱的人应该淘汰的尽可能少,所以弱的和弱的打,强的和强的打,对于有一个弱的和一个强的的情况单独处理,就可以使用dfs的思路,统计每次的剩下的强和弱 人的个数,然后继续递归,直到这个人获胜



代码

#include<iostream>
#include<cstring>
#include<map>
using namespace std;
#define x first
#define y second
typedef pair<int ,int > PII;
#define int long long
int r,k;
double p;
map<PII ,double > mp;

double dfs(int higher,int lower){
    if(higher == 0&& lower == 0) return 1;  // 没人, 必胜
    if(mp.count({higher,lower})) return mp[{higher,lower}]; // 记忆化搜索
    auto &v = mp[{higher,lower}];
    if(lower & 1){ // 弱的人为奇数,那么和一个弱的人比 获胜概率p 
        return v = p * dfs(higher/2,lower/2);  
    }
    else{
        // 如果 弱的人是偶数,还是和弱的人比,但是就会有一个弱的和强的比较,对应不同的概率和剩余人数
        if(lower != 0){
            return v = p * (p * dfs(higher/2 + 1,lower/2 - 1) + (1 - p) * dfs(higher/2,lower/2));
        }
        else{ //如果没有 弱的人,那么只会和强的比较
            return v = (1 - p) * dfs(higher/2,0);
        }
    }
}

signed main(){
    int T;
    cin>>T;
    while(T--){
        mp.clear();
        scanf("%lld%lld%lf",&r,&k,&p);
        r = 1ll<<r; // 总人数
        int higher = k - 1,lower = r - k; //强的人数和弱的人数
        if(p < 0.5) swap(higher,lower), p = 1 - p; // 如果概率小则交换
        double res = dfs(higher,lower);
        printf("%.6lf\n",res);
    }
    
    return 0;
}



F. The Chosen One



题意

有n个人站成一排,每次会淘汰奇数位置的人,然后在剩余人中继续淘汰,只到最后一个人为为止,求最后一个人的编号



思路

我们在每k次淘汰中只会剩下 2

k

的倍数的人,那么最后一次人的编号就是2

k

,且不存在他的倍数,即不存在2

k+1

这个编号



代码

T = int(input(""))
while T:
    T -= 1
    n = int(input(""))
    ans = 1
    while ans * 2 <= n:  # 存在就一直运算
        ans *= 2
    print(ans)



H. The Game of Life



题意

给定几个细胞在无限大的平面上扩展,默认其余细胞都是死的,求在0 – 321次中最多的细胞是哪次,个数为多少,321次有多少个细胞

细胞的成活满足

1.如果活细胞的周围小于2个或大于3个那么他就会死

2.活细胞只有周围有2个或三个才能存活到下一次

3.死细胞的周围有精确地3个就活



思路

就是简单的模拟,但是卡常,一直过不去,参考别人的代码写的



代码

#include<iostream>
#include<cstring>
#include<set>
#include<vector>
using namespace std;
#define x first
#define y second
typedef pair<int ,int > PII;
const int N = 1010,M = 350;
bool a[N][N];
set<PII> s;
vector<PII > live,died;
int dx[] = {1,-1,0,0,1,1,-1,-1};
int dy[] = {0,0,1,-1,1,-1,1,-1};
char str[10];
void insert(int x,int y){
    for(int i = -1;i<=1;i++)
        for(int j = -1;j<=1;j++)
            s.insert({x + i,y + j});
}
int get(int x,int y){
    int t = 0;
    for(int i = 0;i<8;i++){
        int p = x + dx[i],q = y + dy[i];
        if(a[p][q]) t++; 
    }
    return t;
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        memset(a,0,sizeof a);
        s.clear();
        int n,m;
        scanf("%d%d",&n,&m);
        int sum = 0;
        for(int i = 0;i < n; i++){
            scanf("%s",str);
            for(int j = 0;j < m; j++)
            {
                if(str[j] == '#'){
                    int x = i + M,y = j + M;
                    insert(x,y);
                    sum ++;
                    a[x][y] = true;
                }
            }

        }
        int max_id = 0,max_s = sum;
        for(int i = 1;i <= 321;i++){
            live.clear();
            died.clear();
            for(auto it:s){
                int x = it.x,y = it.y;
                if(a[x][y]){
                    int t = get(x,y);
                    if(t == 2||t == 3) continue;
                    died.push_back({x,y});
                    sum--;
                }
                else{
                    int t = get(x,y);
                    if(t == 3) live.push_back({x,y}),sum++;
                }
            }
            if(sum > max_s) max_id = i,max_s = sum;
            for(auto it:live){
                insert(it.x,it.y);
                a[it.x][it.y] = true;
            }
            for(auto it:died){
                a[it.x][it.y] = false;
            }
            
        }
        printf("%d %d %d\n",max_id,max_s,sum);
    }
    
    return 0;
}



I. Rake It In



题意

给定一个4 * 4的地方,有两个人,他们分别选一块2 * 2的地方,并把上面的分数加到总分且这四个单元格会逆转,每个人分别操作k次第一个人想让总分最大,第二个人想让总分最小,求最后的总分数



思路

因为每次的范围很小很小,而且单元的也很小就可以直接考虑爆搜,如果第一个人就选择地方的分数 + 他和第二个人之后的操作分数 中最大的那个,第二个也是同样,只不过找分数最小的地方



代码

#include<iostream>
#include<cstring>
using namespace std;
const int N = 10;
int g[N][N];

int n;

int dfs(int u){ // u为偶数则认为是第一个人 反正第二个人
    if(u == 2 * n) return 0;
    int res = 0;  // 第一人最大化答案
    if(u%2) res = 0x3f3f3f3f; // 第二人 最小化答案
    for(int i = 0;i<=2;i++)
        for(int j = 0;j<=2;j++)
        {
            // 逆时针旋转
            int a = g[i][j],b = g[i][j + 1];
            int c = g[i+1][j],d = g[i + 1][j + 1];
            g[i][j] = b,g[i][j + 1] = d,g[i + 1][j + 1] = c,g[i+1][j] = a; //
            if(u%2) res = min(res,dfs(u + 1) + a + b + c + d);
            else res = max(res,dfs(u + 1) + a + b + c + d);
            // 恢复现场
            g[i][j] = a,g[i][j+1] = b,g[i + 1][j] = c,g[i + 1][j + 1] = d;
        }
    
    return res;
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i = 0;i<4;i++)
            for(int j = 0;j<4;j++)
                scanf("%d",&g[i][j]);
        cout<<dfs(0)<<endl;
    }
    
    return 0;
}



J. Rearrangement



题意

给定一个2 * n的序列,请对它们重新排列,使得相邻的两个数的之和不是三的倍数



思路

我们可以间接的想,只用考虑%3的余数,其中余数为1和为2的不能相邻,但是他们可以和余数为0的相邻,余数为0的只要不和自己相邻即可

首先,考虑余数为0的不能自己相邻,即余数为0的个数只用不大于n即可

其次,考虑余数为1和为2的不能相邻,那么我们可以把余数为1的放在一边,余数为2的放在另一边中间使用两个余数为0的建立分割线,(剩余的余数为0的可以按照余数为1或余数为2分别处理)如果余数为0的小于两个且余数为1和为2的都存在那么他们必然相邻,

特殊情况 只有2个余数为0时,余数为1的个数必须为奇数,因为隔离的两边都能只能有奇数个

1 1 1 0 2 2
1 1 0 2 2 2

或者

1 1 0 2 2 2
1 1 1 0 2 2

都是奇数个,余数为0大于三可以给余数为1或2自由分配这不需要考虑这种情况



代码

#include<iostream>
#include<cstring>
using namespace std;
int cnt[3];
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n;
        memset(cnt,0,sizeof cnt);
        scanf("%d",&n);
        for(int i = 1;i<=2 * n;i++){
            int x;
            scanf("%d",&x);
            int v = x%3;
            cnt[v]++; // 统计各自的个数
        }
        bool flag = true;
        if(cnt[0] > n){ // 余数为0大于n
            flag = false;
        }
        if(cnt[0] <= 2 &&(cnt[1]&&cnt[2])){ 
            if(cnt[0] < 2) //小于2且余数为1,2都存在
                flag = false;
            if(cnt[0] == 2 &&(cnt[1]%2 == 0&&cnt[2]%2==0)){ //等于2 他们都为偶数
                flag = false;
            }
        }

        if(flag) puts("YES");
        else puts("NO");
    }
    
    return 0;
}



L. Twice Equation



题意

给出L,求不小于L的满足 2m(m + 1) = n(n + 1)的最小m



思路

不会推公式,打表加在

推公式网站

上搜

公式 a

0

= 0,a

1

= 3, a

n

= 6 * a

n-1

– a

n-2

+ 2



代码

T = int(input(""))
while T:
    T -= 1
    n = int(input(""))
    a = 0
    b = 3
    while a < n:
        t = 6 * b - a + 2
        a = b
        b = t
    print(a)



M. The Maximum Unreachable Node Set



题意

给定一个DAG(有向图),求有向图的最大独立集,即最大的不可达点集



思路

总个数 – 最小覆盖数,最小覆盖数就是二分图的最大匹配数

可以先用Floyd算法求出所有点的可达点



代码

/*
    最小点覆盖   == 最大匹配
*/
#include<iostream>
#include<cstring>
using namespace std;
const int N = 110;
int match[N];
bool st[N];
bool g[N][N];
int n,m;
bool find(int u){
    for(int i =1;i<=n;i++){
        if(!g[u][i]||st[i]) continue;
        int t = match[i];
        st[i] = true;
        if(t == 0||find(t)){
            match[i] = u;
            return true;
        }
    }
    return false;
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        memset(match,0,sizeof match);
        memset(g,0,sizeof g);
        scanf("%d%d",&n,&m);
        while(m--){
            int a,b;
            scanf("%d%d",&a,&b);
            g[a][b] = true;
        }
        for(int k = 1;k<=n;k++)
            for(int i = 1;i<=n;i++)
                for(int j = 1;j<=n;j++)
                    g[i][j]|=g[i][k]&g[k][j];
        int res = 0;
        for(int i = 1;i<=n;i++){
            memset(st,0,sizeof st);
            if(find(i)) res++;
        }
        printf("%d\n",n - res);
        
    }    
    return 0;
}



版权声明:本文为qq_45074215原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。