2021暑期个人赛2补题 (待补充)

暑期个人赛2补题

D - Kefa and Dishes

CodeForces - 580D

状态压缩dp

思路:看数据范围可以猜出是状态压缩dp,n和m最多18,所以我们用一个20位的二进制数来表示所有状态,f[i, j]表示此时的状态是i且最后吃的菜是j,那么接下来来考虑转移。对于每个f[i, j],我们遍历i的二进制位,假如此时j的那一位是1即这个状态是已经吃了j了,那么说明是一个合法状态,可以转移。

然后考虑可以转移给哪些状态。之后吃的一定是还没有吃过的菜,所以我们就去遍历所有菜,找到一个没吃菜k,然后将f[i, j]转移过去。方程:

f[i ^ (1 << k), k] = max(f[i ^ (1 << k), k], f[i, j] + w[j, k] + a[k])

最后因为我们只需要吃m道菜,遍历所有状态的i,找到符合的取一个max。

不要忘记初始化,f[1 << i, i]表示吃了第i道菜,它应该等于第i道菜的幸福值。

code
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

int n, m, k;
int a[20];
int w[20][20];
LL f[1 << 20][20];
LL ans;

int lowbit(int x)
{
    return x & -x;
}

int cal(int u)
{
    int cnt = 0;
    while (u)
    {
        cnt ++ ;
        u -= lowbit(u);
    }
    
    return cnt;
}

int main()
{
    cin >> n >> m >> k;
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    while (k -- )
    {
        int x, y, c;
        cin >> x >> y >> c;
        w[x][y] = c;
    }
    //cout << w[0][1] << ' ' << w[3][2] << endl;
    for (int i = 1; i <= n; i ++ ) f[1 << i][i] = a[i];//初始化
    n ++ ;
    for (int i = 1; i < (1 << n); i ++ )
        for (int j = 1; j <= n; j ++ )
            if ((i >> j) & 1)//i状态确实吃了第j道菜即合法状态
            {
                for (int k = 0; k < n; k ++ )
                    if (((i >> k) & 1) == 0)//找到一个i状态下还没吃的
                        f[i ^ (1 << k)][k] = max(f[i ^ (1 << k)][k], f[i][j] + w[j][k] + a[k]);
            }

    for (int i = 1; i < (1 << n); i ++ )
        if (cal(i) == m)//看状态i是不是有m个1即是不是吃了m道菜
        {
            for (int op = 0; op < n; op ++ )
                ans = max(ans, f[i][op]);
        }
        
    cout << ans << endl;
    
    return 0;
}

F - Vacations

CodeForces - 698A

贪心 / 线性dp都能做

1.贪心做法

我们从头到尾遍历,能不休息就不休息。假设第i天可以不休息,接下来分类讨论:

  1. i + 1 天是休息日 那么我们如果不让它休息的话,结果一定更优
  2. i + 1 天和第i天活动一样,因为我们不能连续两天都工作一样的,所以这两天我们一定可以挑出一天来工作,那么以我们上述贪心策略来看,不会影响到最优解。
  3. i + 1 天和第i天活动不一样,那么既然可以不休息,那么不休息一定会有更优的答案。

至此,贪心思路正确性证明结束,线性扫描一下即可。

code
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 110;

int a[N];
int ans;
int n;

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    
    for (int i = 1; i <= n; i ++ )//遍历一遍
    {
        if (!a[i]) ans ++ ;//如果是休息日,就休息
        else if (a[i] == 1)//如果是活动1
        {
            if (a[i + 1] == 3) a[i + 1] = 2;//后面如果是3,那么就变成2
            else if (a[i + 1] == 1) a[i + 1] = 0;//如果也是1,那么i+1天就休息
            continue;
        }
        if (a[i] == 2)
        {
            if (a[i + 1] == 3) a[i + 1] = 1;
            else if (a[i + 1] == 2) a[i + 1] = 0;
        }
    }
    
    //for (int i = 1; i <= n; i ++ ) cout << a[i] << ' ';
    //puts("");
    
    cout << ans << endl;
    
    return 0;
}
2.线性dp

f[i, j]表示第i天的状态是j的时候需要休息的最少的天数

#include <bits/stdc++.h>

using namespace std;

const int N = 110;

int n;
int f[N][4];
int a[N];

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    
    memset(f, 0x3f, sizeof f);
    f[0][0] = f[0][1] = f[0][2] = 0;
    
    for (int i = 1; i <= n; i ++ )
    {
        f[i][0] = min(f[i - 1][0], f[i - 1][1]);
        f[i][0] = min(f[i][0], f[i - 1][2]);
        f[i][0] ++ ;
        
        if (a[i] == 1)
            f[i][1] = min(f[i - 1][0], f[i - 1][2]);
        else if (a[i] == 2) 
            f[i][2] = min(f[i - 1][0], f[i - 1][1]);
        else if (a[i] == 3)
        {
            f[i][1] = min(f[i - 1][0], f[i - 1][2]);
            f[i][2] = min(f[i - 1][0], f[i - 1][1]);
        }
    }
    
    //cout << f[1][2] << endl;
    //cout << f[2][2] << endl;
    
    int ans = min(f[n][0], f[n][1]);
    ans = min(ans, f[n][2]);
    cout << ans << endl;
    
    return 0;
}

L - Maximum path

CodeForces - 762D

posted @ 2021-08-02 00:11  sunnyday0725  阅读(31)  评论(0编辑  收藏  举报