ICPCノースセントラルNAコンテスト2017(ソリューションの一部)

目次

総括する:

化学量論

1.トピックの主なアイデア:

2.分析:

3.コードの実装:

スムーズな配列

1.トピックの主なアイデア:

2.分析:

3.コードの実装:


総括する:

私はとても良いです、これはAKでなければなりません...

化学量論

1.トピックの主なアイデア:

化学方程式のバランス

2.分析:

直接ガウス除去。タイトルには一意の最小解が含まれていると記載されているため、空き要素が1つしかないことを意味します。各係数が整数になるように、空き要素の値を列挙します。

3.コードの実装:

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

const double eps = 1e-8;

int len;
struct node
{
    int sgn, n;
    string ele[20];
    int num[20];
}s[20];

int cnt;
map <string, int> id;

double a[10][10];
int x[10];

void read()
{
    while(cin >> s[len].sgn >> s[len].n && s[len].sgn && s[len].n)
    {
        for(int i = 0; i < s[len].n; ++i)
        {
            cin >> s[len].ele[i] >> s[len].num[i];
            if(!id.count(s[len].ele[i]))
                id[s[len].ele[i]] = cnt++;
        }
        ++len;
    }
}

void build()
{
    for(int i = 0; i < len; ++i)
    {
        for(int j = 0; j < s[i].n; ++j)
        {
            a[id[s[i].ele[j]]][i] += s[i].num[j] * s[i].sgn;
        }
    }
}

int gauss()
{
    int c, r;
    for(c = 0, r = 0; c < len; ++c)
    {
        int t = r;
        for(int i = r; i < cnt; ++i)
        {
            if(fabs(a[i][c]) > fabs(a[t][c]))   t = i;
        }
        if(fabs(a[t][c]) < eps) continue;
        for(int i = c; i < len; ++i) swap(a[t][i], a[r][i]);
        for(int i = len - 1; i >= c; --i) a[r][i] /= a[r][c];
        for(int i = 0; i < cnt; ++i)
        {
            if(i == r)  continue;
            if(fabs(a[i][c]) > eps)
            {
                for(int j = len - 1; j >= c; --j)
                {
                    a[i][j] -= a[r][j] * a[i][c];
                }
            }
        }
        ++r;
    }
}

bool check(int x)
{
    double s;
    for(int i = 0; i < len; ++i)
    {
        s = -a[i][len - 1] * x;
        if(abs(int(s + eps) - s) > eps)
            return 0;
    }
    return 1;
}

void work()
{
    for(int i = 1; ; ++i)
    {
        if(check(i))
        {
            for(int j = 0; j < len - 1; ++j)
                printf("%d ", (int)(-a[j][len - 1] * i + eps));
            printf("%d\n", i);
            return;
        }
    }
}

int main()
{
    read();
    build();
    gauss();
    work();
    return 0;
}

スムーズな配列

1.トピックの主なアイデア:

長さnの配列aの場合、各番号を任意の値に変更できるようになりました。これにより、k個の連続する番号ごとの合計がsになり、変更の最小数がわかります。

2.分析:

a [i] == a [i + j * k]であることは簡単にわかります。

次に、数値をk個のグループに分割し、問題をグループ内の要素を等しくするように変換します。各グループ内の要素の合計は、sの最小変更時間です。

明らかにDP、dp [i] [j]は、最初のiグループが考慮されることを意味し、最初のiグループの要素の合計は、sの変更がない最大回数です。

最終的な答えはn-dp [k-1] [s]です。

状態の計算は非常に単純で、それについて話すのは面倒です。

3.コードの実装:

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

const int M = (int)5e3;
const int inf = 0x3f3f3f3f;

int a[M + 5];
int cnt[M + 5];
vector <int> v;
int dp[2][M + 5];

int main()
{
    int n, k, s;
    scanf("%d %d %d", &n, &k, &s);
    for(int i = 0; i < n; ++i)
        scanf("%d", &a[i]);
    memset(dp, -inf, sizeof(dp));
    for(int i = 0; i < k; ++i)
    {
        v.clear();
        memset(cnt, 0, sizeof(cnt));
        for(int j = 0; i + j * k < n; ++j)
        {
            if(cnt[a[i + j * k]] == 0)
            {
                v.push_back(a[i + j * k]);
            }
            ++cnt[a[i + j * k]];
        }
        if(i == 0)
        {
            for(int j = 0; j <= s; ++j)
                dp[i & 1][j] = cnt[j];
        }
        else
        {
            int mx = -inf;
            for(int j = 0; j <= s; ++j)
            {
                dp[i & 1][j] = -inf;
                for(auto x: v)
                {
                    if(j >= x)
                        dp[i & 1][j] = max(dp[i & 1][j], dp[i - 1 & 1][j - x] + cnt[x]);
                }
                mx = max(mx, dp[i - 1 & 1][j]);
                dp[i & 1][j] = max(dp[i & 1][j], mx);
            }
        }
    }
    printf("%d\n", n - dp[k - 1 & 1][s]);
    return 0;
}

 

おすすめ

転載: blog.csdn.net/The___Flash/article/details/104598915