ACM暑期训练记录2

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_37517391/article/details/81701989

A.Abstract Picture Gym - 100917A

分析:由于最后刷的一笔肯定使得某一行或者是某一列均为相同的颜色。
因此我们可以在一开始找到所有的行或者列,他们的颜色全都一样,把这样的行或列加入到队列里面去。
我们处理在队列里面的行或者列:把它取出来,并且把该笔刷成的颜色全都变成’?’(即可变颜色)。然后检查是否有新的行或者列变成颜色均相同了,如果有的话,继续加入队列中进行处理。
队列出队的顺序反过来即为答案。
具体实现细节略。


F - Find the Length Gym - 100917F

分析:因为不存在重复边,那么一个最短简单环,至少需要有三个点组成,而删掉最短简单环上的一条边 u v 那么剩下的一条路径一定是删去 u v 之后的从 u v 的最短路。
对于一定包含点 s 的最短简单环,我们枚举这里面假装删掉的那条边,那么会出现下面两种情况
一、这条边的一个端点是 s
这种情况下,设另一端的端点为 t ,如果 s t 的最短路就不是这条边,那么很好,从 s t 的最短路 + s t 就是一个可行结果,更新包含 s 的最短简单环答案;而如果从 s t 的最短路就是这条边,那么这种情况我们直接扔掉,不做处理,因为这种情况可以由枚举其他边的时候解决。
二、这条边的两个端点 u v 均非 s
这种情况下,从 u v 的最短路还要包含 s ,那么只能是从 u s 的最短路 + v s 的最短路,这里千万要注意两条最短路不能有重复边。否则从 u v 的最短路将不会包含点 s
具体做法:
枚举点 s ,跑一边Dijkstra,得到一颗最短路径树,然后根据第一种情况更新一次答案。根据第二种情况更新答案的时候注意,判断两条路不包含重复边的方法可以用 l c a ,当然也可以用其他的方法。

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
typedef pair<int,int> pii;
const int N = 400;
const int inf = 1e8;
int dist[N];
int G[N][N];
int n;
int fa[N];
int find(int x){
    return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void dij(int s){
    for(int i = 1;i <= n;++i) dist[i] = inf,fa[i] = i;
    dist[s] = 0;
    priority_queue<pii> Q;
    Q.push({0,s});
    while(!Q.empty()){
        pii p = Q.top();Q.pop();
        int u = p.second;
        int d = -p.first;
        if(d > dist[u]) continue;
        for(int v = 1;v <= n;++v){
            if(v == u) continue;
            if(dist[v] > dist[u] + G[u][v]){
                dist[v] = dist[u] + G[u][v];
                if(u != s) fa[v] = u;
                Q.push({-dist[v],v});
            }
        }
    }
}
#define pr(x) cout<<#x<<":"<<x<<endl
int main(){
    cin>>n;
    for(int i = 1;i <= n;++i){
        for(int j = 1;j <= n;++j){
            scanf(" %d",&G[i][j]);
            if(G[i][j] == -1) G[i][j] = inf;
        }
    }
    for(int s = 1;s <= n;++s){
        int ans = inf;
        dij(s);
        for(int i = 1;i <= n;++i){
            if(fa[i] != i){
                if(dist[i] + G[i][s] < ans)
                    ans = dist[i] + G[i][s];
            }
        }
        for(int i = 1;i <= n;++i){
            if(i == s) continue;
            for(int j = 1;j <= n;++j){
                if(j == s || find(i) == find(j)) continue;
                if(dist[i]+dist[j]+G[i][j] < ans)
                    ans = dist[i]+dist[j]+G[i][j];
            }
        }
        ans = ans == inf ? -1:ans;
        printf("%d\n",ans);
    }

    return 0;
}

I - Interactive Casino Gym - 100917I

这道题很有意思。
我们把所有的种子每个都用随机数生成器计算出前几个值,并把每个值得到的输赢用01串存起来,实践发现当计算到第38层的时候,我们就可以把每个种子的01串给区分开了。对于每个种子,01串的长度为38。
我们将这所有的01串放入一个Trie树里面去。一开始一直用猜1来试,可以根据反馈结果确定这次的位。38轮以后我们就可以找得到随机数种子了。找到随机数种子这道题基本就结束了。


J - Judgement Gym - 100917J

我们用背包dp求出在第一种方案下的权值和为 s 1 时候所能得到第二种方案下权值和的最大值 s 2 ,只要出现 s 1 < p s 2 >= q 的情况,那么就输出”NO”并且打印方案。反过来再做一遍就好了。
这里注意这道题要使用滚动数组,这样的话记录方案就会变得麻烦,为了保存信息,需要使用一个长为100的bitset来存方案。

#include <iostream>
#include <cstdio>
#include <assert.h>
#include <cstring>
#include <bitset>
using namespace std;
const int N = 1e6+10; 
int n,upb[2],val[2][N];
int dp[2][N];
bitset<101> way[2][N];
int vis[111];
int main(){
    scanf("%d",&n);
    scanf("%d",&upb[0]);
    for(int i = 1;i <= n;++i) scanf("%d",&val[0][i]);
    scanf("%d",&upb[1]);
    for(int i = 1;i <= n;++i) scanf("%d",&val[1][i]);
    int cc = 0 ;
    for(int cas = 0;cas < 2;++cas){
        for(int i = 1;i <= n;++i){
            for(int j = upb[cas]-1;j >= val[cas][i];j--){
                if(dp[cas][j-val[cas][i]] + val[cas^1][i] > dp[cas][j]){
                    dp[cas][j] = dp[cas][j-val[cas][i]] + val[cas^1][i];
                    //map[cas][j] = i;
                    way[cas][j] = way[cas][j-val[cas][i]];
                    way[cas][j].set(i); 
                    if(dp[cas][j] >= upb[cas^1]){
                        //output(cas,j);
                        puts("NO");
                        for(int i = 1;i <= n;++i){
                            cout<<way[cas][j][i];
                        }
                        return 0;
                    }
                }
            }
        }
    }
    puts("YES");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_37517391/article/details/81701989