Problem
甲乙两人要合唱一首有m个音符的歌,歌中每个音符音高都是1~n之间的正整数。
甲能够唱音高在[1,b]范围内的音符,乙能够唱音高在[a,n]范围内的音符。
现在两个人要合唱这首歌,要满足两个条件:
1、一个人不能唱他不能唱的音符。
2、为了保持韵律的和谐,所有音高相同的音符都要由同一个人演唱。
你所要求的东西在“编程任务”标题中。
对于给定的歌曲和甲乙能演唱的音高范围,计算出一种符合以上两个条件的歌唱方案使得甲乙之间切换的次数尽量少,所谓切换,就是指唱当前音符的人和唱下一个音符的人不一样,你只需输出最少的切换次数即可。
Hint
对于20%的数据,n<=15。
对于50%的数据,n<=50。
对于100%的数据,n,m<=1000。
Solution
这道题我刚看以为水题,结果发现有第二个条件。。。
然后我想不到该怎么破,于是考虑打暴力。。。
子任务中的“n<=15”又错看成“m<=15”,于是数组又开小了,RE。。。
那么这道题的正解其实是网络流。
设甲为源点S,乙为汇点T。对于每个音高,我们都给它设一个点。
对于某个音高x,如果它能被甲唱,就从S向它连一条容量为1的边,表示让甲唱它有1的代价;否则就连一条容量为inf的边。如果它能被乙唱,就从它向T连一条容量为1的边,否则连容量为inf的边。
对于相邻的两个音符,比如i-1和i,设它们的音高分别为a,b,我们就在a和b之间连一条无向边。那么如果我们割了S到a的边,又割了b到T的边,那么a和b之间的边你也得割一条。
于是,直接跑最大流,最后输出答案-n。
那么其实我们可以不必这么麻烦,我们可以只连inf的边和相邻音符间的边,这样答案就不用-n了。
时间复杂度:
。
Code
这次我打的又是SAP和dinic的结合版,看不懂的可以看一看这篇博客的代码。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define fo(i,a,b) for(i=a;i<=b;i++)
const int N=1010,M=N<<3,inf=0x3FFFFFFF;
int t,i,n,m,a,b,tot,tov[M],len[M],next[M],last[N],S,T,x,la,node,dis[N],cur[N],GAP[N],ans;
inline void link(int x,int y,int z)
{
tov[++tot]=y;
len[tot]=z;
next[tot]=last[x];
last[x]=tot;
}
inline void makeside(int x,int y,int z1,int z2)
{
link(x,y,z1);
link(y,x,z2);
}
void scan()
{
scanf("%d%d%d%d",&n,&m,&a,&b);
tot=1;
memset(tov ,0,sizeof tov );
memset(len ,0,sizeof len );
memset(next,0,sizeof next);
memset(last,0,sizeof last);
S=0;T=n+1;
fo(i,b+1,n )makeside(S,i,inf,0);
fo(i,1 ,a-1)makeside(i,T,inf,0);
fo(i,1,m)
{
scanf("%d",&x);
if(i>1)makeside(la,x,1,1);
la=x;
}
}
void init()
{
ans=0;
memset(dis,0,sizeof dis);
memset(cur,0,sizeof cur);
memset(GAP,0,sizeof GAP);
GAP[0]=node=T+1;
}
int flow(int x,int Flow)
{
if(x==T)return Flow;
int i,y,have=0,now;
for(i=cur[x];i;i=next[i])
if(dis[y=tov[i]]+1==dis[x]&&len[i])
{
cur[x]=i;
now=flow(y,min(len[i],Flow-have));
len[i]-=now,len[i^1]+=now;
if((have+=now)==Flow)return have;
}
cur[x]=last[x];
if(!--GAP[dis[x]])dis[S]=node;
GAP[++dis[x]]++;
return have;
}
int main()
{
freopen("sing.in","r",stdin);
freopen("sing.out","w",stdout);
for(scanf("%d",&t);t;t--)
{
scan();
init();
while(dis[S]<node)ans+=flow(S,inf);
printf("%d\n",ans);
}
}