20220729 - DP训练 #2

20220729 - DP训练 #2

时间记录

  1. \(8:00-8:10\) 浏览题面

  2. \(8:10-8:50\) T1

    看题想到了建树,从每一个点遍历,若能遍历每一个点,则可以获胜

    快速写完之后,发现每次格斗只能和相邻的人进行对决

    再思考,感觉像是 区间DP,但是忘记怎么写了,于是写搜索

    感觉写出了满分做法,最终爆零。

  3. \(8:50-9:20\) T2

    看出是分组背包,利用前缀和优化,写完感觉没有问题,最终 \(90\) 分。

  4. \(9:20-9:50\) T3

    再次看题,没有思路,假设两种状态均不可行,最终写贪心

    分别按照能够杀掉野怪个数,花费最少排序,最后去两种贪心的最小花费

    没想到居然把样例都过了!!!

  5. \(9:50-10:10\)

    原计划 \(10:00\) 比赛结束,最后延时 \(20\) 分钟

    于是重新检查之前代码,发现 \(T1\) 的搜索可以再优化

    删删减减之后,就不成搜索了。。。

    for循环 就基本可以解决,没有时间再写

最终成绩——前 \(47\%\)

T1 T2 T3
0 90 4

\(T2\) 只差一组数据,和他人代码多次对比,更正,发现是因为 \(sum\) 数组少开了 \(40\)

计算大小错误,导致丢了 \(10\) 分。

\(T3\) 没有想到 状压,而且现在也不会写状压了,重新复习。

T1-格斗

题目描述

格斗俱乐部是格斗爱好者的一个组织,在这里,格斗者们能通过与别的成员进行格斗来释放自己的压力与轻松自己的情绪。

最近俱乐部举行了一场比赛,该比赛有 \(N\) 位选手参加,他们将围成一个圆圈,每一场比赛圈内任意的两位相邻的选手均可进行相互的格斗,胜利者将留在圈内进入下轮比赛而失败者则直接被送往医院(没有平局)。比赛是残酷的,最后圈内将只剩下一位选手,他将是总冠军。

我们做个奇怪的假设,两位选手进行格斗,他们比赛的结果总是确定的。虽然俱乐部的成员们都很喜欢格斗,但是他们仍然很希望能获得总冠军。

现在你通过统计已经知道了任意两位选手格斗的结果,你有责任告诉每位选手,如果赛程合适安排的话,他是否可能成为总冠军。

输入格式

数据第一行是一个整数\(N(1 \leq N \leq 40)\),表示比赛的选手数量。

接下来给出一个 \(N\times N\) 的 “\(0\)”、“\(1\)” 矩阵\(A\)(行内用空格隔开),第 \(i\) 行第 \(j\) 列为 \(1\) 表示选手 \(i\) 能战胜选手 \(j\),否则选手 \(j\) 能战胜选手 \(i\)

你可以假定 \(A_{i,j}\)\(A_{j,i}(i \neq j)\) 均是不同的且 \(A_{i,i}=0\) 。比赛开始时所有选手按顺时针方向由编号 \(1\) 到编号 \(N\) 站成一个圈,初始时编号 \(1\) 与编号 \(N\) 的选手是相邻的。

输出格式

输出包含 \(N\) 行,每行为一个整数 “\(0\)” 或 “\(1\)”,“\(1\)” 表示第 \(i\) 号选手有可能成为冠军,“\(0\)” 表示不可能。

样例数据

input

3
0 1 1
0 0 1
0 0 0

output

1
0
0

本题为 区间DP,由于存在环,将长度扩展一倍即可破环

数据范围很小,可以尝试 三维状态

状态:\(f[i][j][k]\):若从第 \(i\) 个人到第 \(j\) 个人决出胜者,第 \(k\) 个人能否为最终胜者

状态转移:见代码

int m=n<<1;
for(int i=1;i<=m;i++)
    f[i][i][i]=1;
for(int len=2;len<=m;len++)
{
    for(int i=1;i+len-1<=m;i++)
    {
        int j=i+len-1;
        for(int k=i;k<j;k++)
            for(int l=i;l<=k;l++) 
                if(f[i][k][l])
                    for(int r=k+1;r<=j;r++) 
                        if(f[k+1][j][r])
                            if(a[l][r]) f[i][j][l]=true;
                            else f[i][j][r]=true;
    }
}

当然,还有另一种状态,相对于第一种比较难想,不过多阐述。

状态:\(f[i][j]\):若从第 \(i\) 个人到第 \(j\) 个人决出胜者,胜者能否为 \(i\)\(j\)

状态转移:

if((a[i][k]==1||a[j][k]==1)&&f[i][k]==true&&f[k][j]==true) f[i][j]=true;

T2-背包问题

题目描述

\(T\) 组物品中选出一些物品,放入背包中,求剩余空间的最小值。

限制条件:从每组物品中挑选物品必须要选取连续的一段。

就是说,如果这组物品共有 \(n\) 个: 物品 \(1\)、物品 \(2\)、物品 \(3\)\(\cdots\)、物品 \(n\)

那么只能选取物品 \(i\)、物品 \(i+1\)\(\cdots\)、物品 \(j\),其中 \(1 \leq i \leq j \leq n\),或者不选。

输入格式

第一行为两个用空格隔开的正整数 \(v\)\(T\) 。表示背包的空间和物品的组数。

接下来有 \(T\) 行,每行先是一个正整数 \(n_i\),表示这组物品有 \(n_i\) 个,然后 \(n_i\) 个正整数,表示每个物品的大小。

输出格式

仅一个数,表示剩余空间的最小值。

样例数据

input

100 3 
3 7 6 8 
2 80 70 
4 101 108 103 150 

output

6

样例说明

\(1\) 组选 \(6,8\),第 \(2\) 组选 \(80\),第 \(3\) 组不选。

数据规模与约定

\(60\%\) 的数据满足:\(1 \leq n_i \leq 10\)

\(100\%\) 的数据满足:\(1 \leq n_i \leq 100\)\(1 \leq v \leq 5000\)\(1 \leq T \leq 10\)

分组背包 模型,稍有变化,需要利用前缀和进行优化

优化思想与 AcWing277 饼干 类似,可以参考

将问题转化为 可行性问题 即可

状态:\(f[i][j]\):使用前 \(i\) 组物品,能否使占用的总空间为 \(j\)

状态转移:\(f[i][j]|=f[i-1][j-sum[k]]\)

f[0][0]=true;
for(int i=1;i<=T;i++)
{
    int cnt=0;
    for(int l=1;l<=n[i];l++)
    {
        for(int k=l;k<=n[i];k++)
        {
            sum[cnt+1]=sum[cnt]+a[i][k];
            if(k==l) sum[cnt+1]-=sum[cnt];
            cnt++;
        }
    }
    for(int j=0;j<=v;j++)
    {
        f[i][j]|=f[i-1][j];
        for(int k=1;k<=cnt;k++)
            if(j>=sum[k]) f[i][j]|=f[i-1][j-sum[k]];
    }
    for(int j=1;j<=cnt;j++)
        sum[j]=0;
}

T3-Get Everything AtCoder abc142E

题目描述

野区里有 \(n\) 个野怪, 编号为 \(1\)\(n\)

商店里出售 \(m\) 把武器,每一把武器只能打死特定的一些野怪。

\(i\) 把武器花费 \(a_i\) 金币,能打死 \(b_i\) 个野怪,这 \(b_i\) 个野怪的编号分别为 \(c_{i,1},c_{i,2},\cdots ,c_{i,b_i}\),已购买的武器可以使用任意次。

theshy 需要花费最少的钱购买一些武器,将这些野怪全部打死,如果能全部打死,输出这个最小花费;如果不能,输出 \(−1\)

输入格式

第一行输入野怪的个数 \(n\) 和武器的个数 \(m\)

接下来共有 \(m\) 组,第 \(i\) 组描述第 \(i\) 把武器的信息:

对于每一组武器:

第一行输入一个 \(a\)\(b\),分别代表这种武器的价钱和这把武器能打死几个野怪

第二行输入 \(b\) 个编号,代表这个技能能打死野怪的编号

输出格式

输出最小的花费打死所有的野怪。如果不能全部打死,输出 \(-1\)

样例数据

input

2 3
10 1
1
15 1
2
30 2
1 2

output

25

购买第一把武器和第二把武器可以打死所有的野怪,共花费最小消耗 \(25\)

input

12 1
100000 1
2

output

-1

input

4 6
67786 3
1 3 4
3497 1
2
44908 3
2 3 4
2156 3
2 3 4
26230 1
2
86918 1
3

output

69942

数据规模与约定

所有的数都是整数

\(1 \leq n \leq 12\)

\(1 \leq m \leq 1000\)

\(1 \leq a_i \leq 100000\)

\(1 \leq b_i \leq n\)

\(1 \leq c_{i,1} < c_{i,2} < \cdots < c_{i,b_i} \leq n\)

观察数据范围 \(n \leq 12\),范围很小,考虑到 状压DP

套用 状压DP 模板,即可基本解决本题

状态:\(f[mask]\):在当前集合下野怪能否被杀的情况集合

我们可以用:\(a[i]\) 表示第 \(i\) 把武器能够杀死野怪的集合

scanf("%d",&x);
a[i]|=(1<<(x-1));

状态转移:

\[f[tmask] = \min_{1 \leq i \leq m} (f[tmask],f[mask]+val[i]),\ tmask=mask|a[i] \]

其中,\(a[i]\):第 \(i\) 把武器能够杀死野怪的集合

int t=(1<<n)-1;
for(int i=0;i<=t;i++)
    f[i]=INF;
f[0]=0;
for(int mask=0;mask<=t;mask++)
{
    for(int i=1;i<=m;i++)
    {
        int tmask=(mask|a[i]);
        f[tmask]=min(f[tmask],f[mask]+val[i]);
    }
}
if(f[t]==INF) printf("-1\n");
else printf("%d\n",f[t]);
posted @ 2022-07-29 16:25  Lan_Sky  阅读(71)  评论(0)    收藏  举报