题目描述:
甲乙两人要合唱一首有m个音符的歌,歌中每个音符音高都是1~n之间的正整数。
甲能够唱音高在[1,b]范围内的音符,乙能够唱音高在[a,n]范围内的音符。
现在两个人要合唱这首歌,要满足两个条件:
1、一个人不能唱他不能唱的音符。
2、为了保持韵律的和谐,所有音高相同的音符都要由同一个人演唱。
你所要求的东西在“编程任务”标题中。
对于给定的歌曲和甲乙能演唱的音高范围,计算出一种符合以上两个条件的歌唱方案使得甲乙之间切换的次数尽量少,所谓切换,就是指唱当前音符的人和唱下一个音符的人不一样,你只需输出最少的切换次数即可。
题解:
这显然是个流,考虑用经典的二元关系去流。
如果两个颜色x,y相邻,连(1,1)。
如果x只属于甲或只属于乙,S到x连∞的边或x到T连∞的边。
如果它可以属于甲或者乙,则S->x连1,x->T连1,最后ans要减去这个多余的流量。
这是一种建图的方法。
还有一种是按序号建图,相邻的序号要连(1,1).
对于同一音符的序号,它们要不都属于S,要不都属于T。
建新的点a’,对于每个x,到a’连(∞,∞)。
Code:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define mem(a) memset(a, 0, sizeof a)
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
const int N = 1e4 + 5, INF = 1 << 30;
int Q, n, m, a, b, c[N], p[N], td;
int tot, final[N], next[N], to[N], r[N];
int d[N], co[N], cur[N], S, T, ans;
void link(int x, int y, int z, int z2) {
next[++ tot] = final[x], to[tot] = y, r[tot] = z, final[x] = tot;
next[++ tot] = final[y], to[tot] = x, r[tot] = z2, final[y] = tot;
}
int dg(int x, int flow) {
if(x == T) return flow;
int use = 0;
for(int i = cur[x]; i; i = next[i]) {
int y = to[i];
if(d[y] + 1 == d[x] && r[i]) {
int tmp = dg(y, min(r[i], flow - use));
r[i] -= tmp; r[i ^ 1] += tmp; use += tmp;
if(use == flow) return use;
}
}
cur[x] = final[x];
if(!(-- co[d[x]])) d[S] = T;
++ co[++ d[x]];
return use;
}
int cmp(int x, int y) {
return c[x] < c[y];
}
int main() {
freopen("sing.in", "r", stdin);
freopen("sing.out", "w", stdout);
for(scanf("%d", &Q); Q; Q --) {
mem(final); mem(next);
mem(d); mem(co); mem(cur);
tot = 1;
scanf("%d %d %d %d", &n, &m, &a, &b);
fo(i, 1, m) scanf("%d", &c[i]);
S = n + m + 1; T = S + 1;
fo(i, 1, m) p[i] = i; sort(p + 1, p + m + 1, cmp);
int la = 1; td = m;
fo(i, 2, m + 1) {
if(c[p[i]] != c[p[i - 1]]) {
if(c[p[i - 1]] >= a && c[p[i - 1]] <= b) {
td ++;
fo(j, la, i - 1) link(p[j], td, INF, INF);
}
la = i;
}
}
fo(i, 1, m) {
if(c[i] <= b && c[i] < a) link(S, i, INF, 0);
if(c[i] >= a && c[i] > b) link(i, T, INF, 0);
}
fo(i, 2, m) link(i - 1, i, 1, 1);
ans = 0; co[0] = td + 2;
for(; d[S] < T;) ans += dg(S, INF);
printf("%d\n", ans);
}
}