2023.3.14 状压 dp 模拟赛题解
好强啊。
首先 T1 的一个优化 luogu P1842 奶牛玩杂技,需要一个贪心排序来优化序列顺序。关于贪心排序:像这样有两种及以上性质的序列,注意以哪个为关键字排列。两道题的顺序都是:x.s + x.w > y.s + y.w。一定会优先选又重又强壮的奶牛垫在下面。
两种做法:1.dfs。爆搜加上各种优化剪枝就会把时间复杂度降到很小。代码是找同学要的,太强了我今天才刚会写 dfs,这种剪枝第一次见真就叹为观止!
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 22;
int n, flag = 0;
ll h, ans = 0;
int sumh[N], sumw[N];
struct Cow {ll h, w, s;} a[N];
bool cmp(Cow x, Cow y){return x.s + x.w > y.s + y.w;}
void dfs(int i, ll high, ll tot)
{
if(high >= h)
{
flag = 1;
ans = max(ans, tot);
return ;
}
if(i > n) return ;
if(ans >= tot) return ;
if(high + sumh[n] - sumh[i - 1] < h) return ;
if(a[i].w <= tot) dfs(i + 1, high + a[i].h, min(a[i].s, tot - a[i].w));
if(tot) dfs(i + 1, high, tot);
}
int main()
{
scanf("%d%lld", &n, &h);
for(int i = 1; i <= n; i ++ ) scanf("%lld%lld%lld", &a[i].h, &a[i].w, &a[i].s);
sort(a + 1, a + n + 1, cmp);
for(int i = 1; i <= n; i ++ )
{
sumh[i] = sumh[i - 1] + a[i].h;
sumw[i] = sumw[i - 1] + a[i].w;
}
dfs(1, 0, 0x3f3f3f3f);
if(flag) printf("%lld", ans);
else printf("Impossible");
return 0;
}
2.状态压缩 dp
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=21;
const int inf=0x3f3f3f3f;
int n;
ll m, tot, maxn = -1;
int r[N];
struct node {ll h,w,s;} a[N];
bool cmp(node a,node b) {return a.s + a.w > b.s + b.w;}
ll check()
{
ll sum = 0, high = 0, ans = inf;
for(int i = 1; i <= tot; i ++ )
{
sum = 0;
for(int j = i + 1; j <= tot; j ++ ) sum += a[r[j]].w;
if(sum > a[r[i]].s) return -1;
high += a[r[i]].h;
ans = min(ans, a[r[i]].s - sum);
}
if(high < m) return -1;
return ans;
}
int main()
{
scanf("%d%lld",&n,&m);
for(int i = 0; i < n; i ++ ) scanf("%lld%lld%lld", &a[i].h, &a[i].w, &a[i].s);
sort(a, a + n, cmp);
for(int s = 1; s < 1 << n; s ++ )
{
tot = 0;
for(int i = 0; i < n; i ++ ) if(s & 1 << i) r[ ++ tot] = i;
maxn = max(maxn, check());
}
if(!maxn) printf("Impossible\n");
else printf("%lld", maxn);
return 0;
}