「赛后总结」20200906

得分情况

预计得分:\(100 + 100 + 0 \sim 100 = 200 \sim 300\)

实际得分:\(100 + 100 + 60 = 260\)

考场上

开 T1,题目有点难读懂,读了一遍放过去了。

开 T2,读了一遍题感觉需要的用到二分答案,这是一个 log,check 里可能需要用到 upper_bound 或者 lower_bound,共两个 log,没思路了,过。

开 T3,严格次短路,不会,写玄学算法希望多骗点分。

回去看 T1,看懂了题,发现是个 sb 题。写完 T1,在想暴力怎么写,写出来了暴力,拍上了。去想 T2。

感觉需要个 DP,想 DP 怎么写,大样例来了,T1 过了大样例就不拍了,T2 想了一个不知道对错的 DP,过了大样例。

写了个暴力,然后对拍,出了两次锅,一次是造数据的锅了,一次是暴力输入顺序反了。。

T1咒语

每一位是独立的,我们要让每一位的差异尽量小,那么就判断这一位 \(0\)\(1\) 的个数关系即可。

时间复杂度:\(O(nL)\)

Code:

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define MAXN 1001

int n, len, num[MAXN][2];
std::string s;

int main() {
    //freopen("curse.in", "r", stdin);
    //freopen("curse.out", "w", stdout);
    std::cin >> n;
    for (int i = 1; i <= n; ++i) {
        std::cin >> s;
        len = s.length();
        for (int j = 0; j < len; ++j) {
            ++num[j][s[j] - '0'];
        }
    }
    for (int i = 0; i < len; ++i) {
        if (num[i][0] >= num[i][1]) putchar('0');
        else putchar('1');
    }
    puts("");
    //fclose(stdin), fclose(stdout);
    return 0;
}

T2 神光

使得 \(L\) 最小

感觉需要用二分答案。

遇到了怎么 check 的难题。

不知道怎么就想到 DP 上了。

\(f_{i,j}\) 表示用了 \(i\) 次红光,\(j\) 次绿光,下一次在使用某一种光的最远的左端点是哪里。

伪代码:

f[0][0] = 最左边的法坛

在已知左端点的时候能够很容易推出下次的左端点。

伪代码:

f[i][j] = max(upper_bound(排好序的法坛,f[i - 1][j] + x - 1),upper_bound(排好序的法坛,f[i][j - 1] + 2 * x - 1)

时间复杂度:\(O(n^2log^2n)\)

Code:

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define MAXN 2222

int min(int a, int b) { return a < b ? a : b; }
int max(int a, int b) { return a > b ? a : b; }

int n, r, g, a[MAXN];
int f[MAXN][MAXN];

bool check(int x) {
    int s1 = min(n, r), s2 = min(n, g);
    f[0][0] = a[1];
    int stp = min(n, s1 + s2);
    for (int k = 1; k <= stp; ++k) {
        int stop = min(s1, k);
        for (int i = 0; i <= stop; ++i) {
            int j = k - i;
            if (j > s2) continue;
            if (i == 0) {
                int pos = std::upper_bound(a + 1, a + n + 1, f[i][j - 1] + 2 * x - 1) - a;
                if (pos > n) { f[i][j] = a[n + 1]; return true; }
                else f[i][j] = a[pos];
            }
            else if (j == 0) {
                int pos = std::upper_bound(a + 1, a + n + 1, f[i - 1][j] + x - 1) - a;
                if (pos > n) { f[i][j] = a[n + 1]; return true; }
                else f[i][j] = a[pos];
            }
            else {
                int pos = max(std::upper_bound(a + 1, a + n + 1, f[i][j - 1] + 2 * x - 1) - a, std::upper_bound(a + 1, a + n + 1, f[i - 1][j] + x - 1) - a);
                if (pos > n) { f[i][j] = a[n + 1]; return true; }
                else f[i][j] = a[pos];
            }
        }
    }
    return false;
}

int main() {
    scanf("%d %d %d", &n, &r, &g);
    for (int i = 1; i <= n ; ++i) scanf("%d", &a[i]);
    std::sort(a + 1, a + n + 1);
    a[n + 1] = 2147483647;
    if (r + g >= n) {
        puts("1");
        fclose(stdin), fclose(stdout);
        return 0;
    }
    int lft = 1, rght = a[n];
    while (lft <= rght) {
        int mid = (lft + rght) >> 1;
        if (check(mid)) rght = mid - 1;
        else lft = mid + 1;
    }
    std::cout << lft << '\n';
    return 0;
}

T3 迷宫

严格次短路板子,之前没写过(好像写过类似的)。

考场上写了个最短路,然后限制最短路上的一条边不能走再跑最短路,因为每条边能重复经过所以计算了一下每条最短路上的最小值的贡献。

考场代码:

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#include <queue>
#define MAXN 5001
#define inf 2147483647

int min(int a, int b) { return a < b ? a : b; }

typedef std::pair<int, int> pii;
std::priority_queue<pii, std::vector<pii>, std::greater<pii> > q;
int n, m, pre[MAXN], minn, minnn;
int pthn, head[MAXN], dis[MAXN];
bool vis[MAXN], broken[MAXN][MAXN];
struct Edge {
    int next, to, w;
}pth[MAXN * 200];

void add(int from, int to, int w) {
    pth[++pthn].to = to, pth[pthn].next = head[from];
    pth[pthn].w = w, head[from] = pthn;
}

void dij(int s, int typ, int &min_) {
    memset(dis, 0x3f3f3f, sizeof dis);
    memset(vis, 0, sizeof vis);
    dis[s] = 0, pre[s] = s;
    q.push(std::make_pair(dis[s], s));
    while (!q.empty()) {
        pii u = q.top(); q.pop();
        if (vis[u.second]) continue;
        vis[u.second] = 1;
        for (int i = head[u.second]; i; i = pth[i].next) {
            int x = pth[i].to;
            if (broken[u.second][x]) continue;
            if (!vis[x] && dis[x] > u.first + pth[i].w) {
                min_ = min(min_, pth[i].w);
                if (typ == 1) pre[x] = u.second;
                dis[x] =  u.first + pth[i].w;
                q.push(std::make_pair(dis[x], x));
            }
        }
    }
}

int main() {
    scanf("%d %d", &n, &m);
    for (int i = 1, u, v, w; i <= m; ++i) {
        scanf("%d %d %d", &u, &v, &w);
        add(u, v, w), add(v, u, w);
    }
    int min_ = inf;
    dij(1, 1, min_);
    minn = dis[n];
    minnn = dis[n] + 2 * min_;
    int now = n;
    while (pre[now] != now) {
        broken[now][pre[now]] = broken[pre[now]][now]= 1;
        min_ = inf, dij(1, 0, min_);
        if (dis[n] == minn) minnn = min(minnn, dis[n] + 2 * min_);
        else minnn = min(minnn, dis[n]);
        broken[now][pre[now]] = broken[pre[now]][now]= 0;
        now = pre[now];
    }
    std::cout << minnn << '\n';
    return 0;
}

正解#1:
严格次短路板子(有时间补一下学习笔记)
正解#2:

题目要求次短路,我们可以先用 \(Dijkstra\) 算法求出最短路。设最短路长为 \(s1\),与结点 \(n\) 相连的最短边的长度为 \(c\),设 \(s2=s1+2*c\), 则 \(s2\) 就是次短路长度的上界。接下来我们只要进行 \(DFS\) 深搜即可,在搜索过程中利用上界 \(s2\) 进行剪枝,并不断更新 \(s2\),就可以在题目规定的时间内得到结果。

Code:

#include <fstream>
#include <cstring>
using namespace std;
struct dd
{
    int x,y,z;
};
int n,r,head[5001],tail[5001],dis[5001],s1,s2,ans=1000000000;
dd a[200001];
bool b[5001];
void qsort(int l1,int r1)
{
     if (l1>=r1) return;
     int i=l1,j=r1,t=(i+j)/2;
     dd k=a[t]; a[t]=a[i];
     while (i<j)
     {
         while (a[j].x>=k.x&&i<j) --j;
         if (i<j)
         {
             a[i]=a[j];
             ++i;
         }
         while (a[i].x<k.x&&i<j) ++i;
         if (i<j)
         {
             a[j]=a[i];
             --j;
         }
     }
     a[i]=k;
     qsort(l1,i-1); qsort(i+1,r1);
     return; 
}
void dfs(int k,int s3)
{
     if (s3>s1||s3>=ans) return;
     if (k==n&&s3>s2&&ans>s3)
     {
         ans=s3;
         return;
     }
     for (int i=head[k]; i<=tail[k]; ++i)
         dfs(a[i].y,s3+a[i].z);
     return;
}
int main()
{
    ifstream fin;
    fin.open("maze.in");
    ofstream fout;
    fout.open("maze.out");
    fin>>n>>r;
    for (int i=1; i<=r; ++i)
    {
        fin>>a[2*i-1].x>>a[2*i-1].y>>a[2*i-1].z;
        a[2*i].x=a[2*i-1].y; a[2*i].y=a[2*i-1].x;
        a[2*i].z=a[2*i-1].z;
    }
    r*=2;
    qsort(1,r);
    memset(head,0,sizeof(head));
    memset(tail,0,sizeof(tail));
    head[1]=1; tail[n]=r;
    for (int i=1; i<=r; ++i)
    {
        if (i>1&&a[i].x!=a[i-1].x)
            head[a[i].x]=i;
        if (i<r&&a[i].x!=a[i+1].x)
            tail[a[i].x]=i;
    }
    for (int i=1; i<=n; ++i)
        dis[i]=1000000000;
    dis[1]=0;
    memset(b,false,sizeof(b));
    for (int i=1; i<=n; ++i)
    {
        int minn=1000000000,k=0;
        for (int j=1; j<=n; ++j)
            if (dis[j]<minn&&!b[j])
            {
                minn=dis[j]; 
                k=j;
            }
        b[k]=true;
        for (int j=head[k]; j<=tail[k]; ++j)
            if (dis[a[j].y]>dis[k]+a[j].z)
            {
                dis[a[j].y]=dis[k]+a[j].z;
                if (a[j].y==n) s1=a[j].z*2;
            }
    }
    s2=dis[n]; s1+=s2;
    dfs(1,0);
    fout<<ans<<endl;
    fin.close(); fout.close();
    return 0;
}
    
posted @ 2020-09-06 14:56  yu__xuan  阅读(139)  评论(0编辑  收藏  举报