源自oi-wiki
邻项交换法
常与 d p dp dp 结合,先排序消除后效性再 d p dp dp
P1080 [NOIP2012 提高组] 国王游戏
题意:
国王和
n
n
n 个大臣左右手上分别有一个整数,国王在第一位,
n
n
n 在后边拍成一列,一个大臣获得的金币数为:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。
对
n
n
n 个大臣进行排序,最小化奖赏最多的大臣获得的金币
思路:
设排序后第
i
i
i 个大臣左右手上的数分别为
a
i
,
b
i
a_i,b_i
ai,bi ,考虑通过邻项交换法推导贪心策略。
用
s
s
s 表示第
i
i
i 个大臣前面所有人的
a
i
a_i
ai 的乘积,那么第
i
i
i 个大臣得到的奖赏就是
s
b
i
\frac{s}{b_{i}}
bis,第
i
+
1
i+1
i+1 个大臣得到的奖赏就是
s
∗
a
i
+
1
b
i
+
1
\frac{s*a_{i+1}}{b_i+1}
bi+1s∗ai+1。
如果交换前更优当且仅当
m
a
x
(
s
b
i
,
s
∗
a
i
b
i
+
1
)
<
m
a
x
(
s
b
i
+
1
,
s
∗
a
i
+
1
b
i
)
max(\frac{s}{b_i},\frac{s*a_{i}}{b_{i+1}})<max(\frac{s}{b_{i+1}},\frac{s*a_{i+1}}{b_i})
max(bis,bi+1s∗ai)<max(bi+1s,bis∗ai+1)
提出公因式
s
s
s
m
a
x
(
1
b
i
,
a
i
b
i
+
1
)
<
m
a
x
(
1
b
i
+
1
,
a
i
+
1
b
i
)
max(\frac{1}{b_i},\frac{a_i}{b_{i+1}})<max(\frac{1}{b_{i+1}},\frac{a_{i+1}}{b_i})
max(bi1,bi+1ai)<max(bi+11,biai+1)
化成整式
m
a
x
(
b
i
+
1
,
a
i
∗
b
i
)
<
m
a
x
(
b
i
,
a
i
+
1
∗
b
i
+
1
)
max(b_{i+1},a_i*b_i)<max(b_i,a_{i+1}*b_{i+1})
max(bi+1,ai∗bi)<max(bi,ai+1∗bi+1)
然后排序就好了
01背包限制选择时的容量
选择时的容量有限制,不同的物品选择顺序出现了不同结果,不满足无后效性,可以采用贪心策略邻项交换法进行物品排序消除后效性
设当前容量为
j
j
j,物品体积为
p
p
p,满足容量大于等于
q
q
q 时才选择,价值为
w
w
w
- 考虑到状态转移方程 f [ j ] = m a x ( f [ j ] , f [ j − p ] + w ) ( j > = p & & j > = q ) f[j]=max(f[j],f[j-p]+w)(j>=p \ \&\& \ j>=q) f[j]=max(f[j],f[j−p]+w)(j>=p && j>=q)
- 只有保证 f [ j − p ] f[j-p] f[j−p] 最优时方程才能成立
- 设相邻物品 p 1 , q 1 , v 1 p_1,q_1,v_1 p1,q1,v1 和 p 2 , q 2 , v 2 p_2,q_2,v_2 p2,q2,v2
- 若要满足无后效性,也就是交换前更优,需满足 j − p 2 > = q 1 & & j − p 1 > = q 2 j-p_2>=q_1 \ \&\& \ j-p_1>=q_2 j−p2>=q1 && j−p1>=q2
- 即 q 1 − p 1 < = q 2 − p 2 q_1-p_1<=q_2-p_2 q1−p1<=q2−p2
排序后 d p dp dp 即可
美味菜肴
题意:
n
n
n 个食材,
m
m
m 道菜,每道菜美味值为
a
i
a_i
ai,
b
i
b_i
bi 是食材不新鲜的速率,
c
i
c_i
ci 是做这个道菜需要的时间,如果在
t
t
t 时间完成这道菜,那么它的美味值为
a
i
−
t
∗
b
i
a_i-t*b_i
ai−t∗bi。
有
T
T
T 时间做菜,求最大美味值,美味值可能是负数
思路:
邻项交换法,这个比较好推,和国王游戏差不多
初始时间为
0
0
0,考虑前两个
a
1
,
b
1
,
c
1
a_1,b_1,c_1
a1,b1,c1 和
a
2
,
b
2
,
c
2
a_2,b_2,c_2
a2,b2,c2
交换前更优当且仅当
a
1
−
b
1
∗
c
1
+
a
2
−
b
2
∗
(
c
1
+
c
2
)
>
a
2
−
c
2
∗
b
2
+
a
1
−
b
1
∗
(
c
1
+
c
2
)
⟹
b
1
∗
c
2
>
b
2
∗
c
1
a_1-b_1*c_1+a_2-b_2*(c_1+c_2)>a_2-c_2*b_2+a_1-b_1*(c_1+c_2)\\ \Longrightarrow b_1*c_2>b_2*c_1
a1−b1∗c1+a2−b2∗(c1+c2)>a2−c2∗b2+a1−b1∗(c1+c2)⟹b1∗c2>b2∗c1
排序后
d
p
dp
dp 即可
反悔贪心
P2949 [USACO09OPEN]Work Scheduling G
贪大小+并查集
经典的并查集应用
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 2e6 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
struct node{
int d, p;
bool operator<(const node &B){
return p > B.p;
}
}a[maxn];
unordered_map <int, int> ma;
int find(int x){
if(!ma.count(x)) return x;
else return ma[x] = find(ma[x]);
}
void work()
{
cin >> n;
for(int i = 1; i <= n; ++i){
cin >> a[i].d >> a[i].p;
}
sort(a + 1, a + 1 + n);
ll ans = 0;
for(int i = 1; i <= n; ++i){
int x = find(a[i].d);
if(x >= 1) ans += a[i].p, ma[x] = x - 1;
}
cout << ans;
}
int main()
{
ios::sync_with_stdio(0);
// int TT;cin>>TT;while(TT--)
work();
return 0;
}
贪截至时间+反悔贪心
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 2e6 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
struct node{
int d, p;
bool operator<(const node &B){
return d < B.d;
}
}a[maxn];
priority_queue <ll, vector<ll>, greater<ll> > q;
void work()
{
cin >> n;
for(int i = 1; i <= n; ++i){
cin >> a[i].d >> a[i].p;
}
sort(a + 1, a + 1 + n);
ll ans = 0;
for(int i = 1; i <= n; ++i){
if(a[i].d <= q.size()){
if(q.top() < a[i].p){
ans += a[i].p - q.top();
q.pop();
q.push(a[i].p);
}
}
else {
ans += a[i].p;
q.push(a[i].p);
}
}
cout << ans;
}
int main()
{
ios::sync_with_stdio(0);
// int TT;cin>>TT;while(TT--)
work();
return 0;
}
哦唔西迪西小姐
思路:
反悔贪心,但是并不需要优先队列维护,很妙
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define mem(x, d) memset(x, d, sizeof(x))
#define eps 1e-6
using namespace std;
const int maxn = 2e6 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
int a[maxn], p[maxn], b[maxn];
vector <int> v1, v2;
ll f(){// 走 0
vector <int> v;
ll ans = 0;
for(int i = 1; i <= n; ++i){
if(b[i]) v.push_back(max(-p[i], a[i] - p[i]));
else{
ans += max(a[i], 0);//直接选
v.push_back(-p[i] - max(a[i], 0));//
}
}
sort(all(v), greater<int>());
for(int i = 0; i < v.size() && i < m; ++i){
ans += max(0, v[i]);
}
return ans;
}
void work()
{
cin >> n >> m;
for(int i = 1; i <= n; ++i) cin >> a[i];
for(int j = 1; j <= n; ++j) cin >> p[j];
for(int i = 1; i <= n; ++i) cin >> b[i];
ll ans = f();
for(int i = 1; i <= n; ++i) b[i] = !b[i];
ans = max(ans, f());
cout << ans;
}
int main()
{
ios::sync_with_stdio(0);
// int TT;cin>>TT;while(TT--)
work();
return 0;
}