Orz教主第五次模拟
题目一览:
一、最大匹配:https://www.vijos.org/p/1662
二、旅行: https://www.vijos.org/p/1661
三、资源勘探:https://www.vijos.org/p/1663
四、排列统计:https://www.vijos.org/p/1660
一、最大匹配
①题目大意:
从A数组与B数组中各自取出k个数并一一配对,使得配对的元素差的绝对值之和最大
②做题情况:
考虑不清楚是一个很严重的问题——没有去验证方法的正确性就直接开打,从而导致致命错误的出现;没有进行对拍也是一个很失败的地方——如果当时对过拍,恐怕错误就会暴露出来。。。。。。一道最水的题都做不对还谈何拿到高分!?——[当时此题分数为30分]
③题目分析:
AB数组排序后,答案每次加上当前max(abs(maxA-minB),abs(maxB-minA))(应该都知道匹配过的不能再选吧),最后输出即可
④代码:
type
arr=array[0..100001] of int64;
var
n,k,i,l1,l2,r1,r2:longint;
ans,q,p:int64;
a,b:arr;
function max(a,b:int64):int64;
begin
if a>b then exit(a);
exit(b);
end;
procedure qsort(var x:arr; l,r:longint);
var
i,j:longint;
m:int64;
begin
i:=l; j:=r;
m:=x[random(j-i+1)+i];
while i<=j do begin
while x[i]<m do inc(i);
while x[j]>m do dec(j);
if i<=j then begin
x[0]:=x[i];
x[i]:=x[j];
x[j]:=x[0];
inc(i); dec(j);
end;
end;
if i<r then qsort(x,i,r);
if l<j then qsort(x,l,j);
end;
begin
randomize;
readln(n,k);
for i:=1 to n do read(a[i]);
for i:=1 to n do read(b[i]);
readln;
qsort(a,1,n);
qsort(b,1,n);
l1:=1; r1:=n;
l2:=1; r2:=n;
for i:=1 to k do begin
q:=abs(a[r1]-b[l1]);
p:=abs(b[r2]-a[l2]);
if q>p then begin
inc(l1);
dec(r1);
inc(ans,q);
end else begin
inc(l2);
dec(r2);
inc(ans,p);
end;
end;
writeln(ans);
end.
①题目大意:
给出一个数组,做出一些调整后使得相邻两元素差的绝对值总和最小;调整的规则:可以交换相邻两元素的位置,但每次交换的位置必 须在前一次交换的位置之后[比如每次交换了A[j]和A[j+1],那么下次交换A[k]和A[k+1]必须满足j<k]
②做题情况:
思维不能很好的转弯——看出是dp之后就开始想转移方程,但没想过从后往前推会比从前往后推要简单很多。没去从另一个方面去思考真的很糟糕——[当时此题分数为20分]
③题目分析:
从后往前推会比较容易——假设当前位置为i,则只需考虑以下2种情况:
1.a[i]没往后移
2.a[i]往后移
f[i,0]表示i没后移后的最小体力消耗,f[i,1]表示i后移i~n的最小体力消耗,sum[i]表示1~i相邻元素差的绝对值的和,则有以 下几个转移方程:
1.f[i,0]:=min(f[i+1,0]+abs(a[i]-a[i+1]){i+1没后移},f[i+1,1]+abs(a[i]-a[i+2]){i+1后移后,原位置就被i+2所取代});
2.f[i,1]:=min(f[i,1],sum[j]-sum[i+1]+abs(a[i]-a[j])+min(f[j+1,0]+abs(a[i]-a[j+1]),f[j+1,1]+abs(a[i]-a[j+2])));{j=n-1~1}
{sum[j]-sum[i+1]代表i后移到j后i~j-1相邻元素差的绝对值的总和,f[j+1,0]+abs(a[i]-a[j+1]);,f[j+1,1]+abs(a[i]-a[j+2])[这两个分离出来后就会很容易理解,因此不加多解释]}
3.f[i,1]:=min(f[i,1],sum[n]-sum[i+1]+abs(a[i]-a[n]));{i后移到n时的特殊判断}
方程中有一些情况本来应该要分离出来判断,但其中的操作(把a[n+1]赋为a[n])可以使得其少做些判断。最后的答案应为min(f[1,0],f[1,1])
④代码:
var
f:array[0..2001,0..1] of longint;
a,sum:array[0..2001] of longint;
n,i,j:longint;
function min(a,b:longint):longint;
begin
if a<b then exit(a);
exit(b);
end;
begin
readln(n);
for i:=1 to n do read(a[i]);
readln;
for i:=2 to n do sum[i]:=sum[i-1]+abs(a[i]-a[i-1]);
fillchar(f,sizeof(f),$7f shr 1);
f[n,0]:=0; f[n,1]:=0;
a[n+1]:=a[n];
for i:=n-1 downto 1 do begin
f[i,0]:=min(f[i+1,0]+abs(a[i]-a[i+1]),f[i+1,1]+abs(a[i]-a[i+2]));
for j:=i+1 to n-1 do
f[i,1]:=min(f[i,1],sum[j]-sum[i+1]+abs(a[i]-a[j])+min(f[j+1,0]+abs(a[i]-a[j+1]),f[j+1,1]+abs(a[i]-a[j+2])));
f[i,1]:=min(f[i,1],sum[n]-sum[i+1]+abs(a[i]-a[n]));
end;
writeln(min(f[1,0],f[1,1]));
end.
三、资源勘探
①题目大意:
给出一个n×m的矩阵,对于左上角坐标为(1,1)、右下角坐标为(x,y)[x=1~n,y=1~m]的子矩阵,如果在这个子矩阵中恰好只有一个a[i,j(i=1~x,j=1~y)这个值,总答案加一
②做题情况:
心态调整不好会很影响发挥——第二题的打击让我难以静下心来思考第三题——这是十分不好的,如果能调整好心态,以平常心对待题目,分数也就不会在20分止步![当时此题分数为20分]
③题目分析:
f[i,0]表示i这个值在横坐标中最先出现的地方,f[i,1]表示i这个值在横坐标中第二出现的地方,它们的初始值都为m+1,然后从前往后(也就是i=1~n→j=1~m)计算答案。具体流程用图片说明会更加容易明白
注:圆圈为i这个值所在的位置,绿色部分为增加答案的部分,灰色部分为减去答案的部分
初始值都为m+1 最小的横坐标出现使得答案增加(尽管有的不一定合法)
第二小的横坐标出现并减去不合法部分 更新第二小的横坐标并继续减去不合法部分
更新最小的横坐标,增加答案的同时 更新第二小的横坐标并再次减去不合法部分
减去不合法部分
简单的说,就是假设合法的f[i,0]~f[i,1]-1和这段格子以下的矩形部分都是答案,然后在遇到矛盾部分(出现的i的横坐标比f[i,0]或f[i,1]小)后再减去不合法的部分,最后所得就为答案
④代码:
const
mo=19900907;
var
f:array[0..1210001,0..1] of int64;
n,m,i,j,a,k:longint;
ans:int64;
begin
readln(n,m);
for i:=1 to n*m do begin
f[i,0]:=m+1;
f[i,1]:=m+1;
end;
for i:=1 to n do begin
for j:=1 to m do begin
read(a);
k:=n-i+1;
if j<=f[a,0] then begin
ans:=ans+(f[a,0]-j)*k;
ans:=ans-(f[a,1]-f[a,0])*k;
ans:=(ans+mo) mod mo;
f[a,1]:=f[a,0];
f[a,0]:=j;
end else if j<=f[a,1] then begin
ans:=ans-(f[a,1]-j)*k;
ans:=(ans+mo) mod mo;
f[a,1]:=j;
end;
end;
readln;
end;
writeln(ans);
end.
四、排列统计(尚未AC,之后再完善)
①题目大意:
②做题情况:
③题目分析:
④代码:
五、总结:
这一套题并不算难(毕竟是前几年的题),但在这一次测试中暴露出来的问题还是十分严重的!虽说隔了一段时间没有在限定时间内做题并评测,但这出错率还是太过惊人了……一定要在这一次测试中吸取教训,在不同的题上出现的问题要及时改掉,以防在真正的比赛结束之后懊悔地说:“啊!我做错了!”
剩下的时间已不算多了,接下来要更加努力才行啊!