题解

作业调度方案

https://vijos.org/p/1314

难点提要:1 题意。 2 无。

题意:

好好看题就可理解。

n个工件 m个机器 每个工件有m个工序,每个工件的每个工序需要不同的机器来做(也就是所谓的工序的机器号)

对于安排顺序。只要每个工件的工序顺序对了即可。

也就是说一号工件的工序1完成后才能完成一号工件的工序2

                               2                                       3

当一个操作插入到某台机器的某个空档时(机器上最后的尚未安排操作的部分也可以看作一个空档),可以靠前插入,也可以靠后或居中插入。为了使问题简单一些,我们约定:在保证约束条件(1)(2)的条件下,尽量靠前插入。并且,我们还约定,如果有多个空档可以插入,就在保证约束条件(1)(2)的条件下,插入到最前面的一个空档。于是,在这些约定下,上例中的方案一是正确的,而方案二是不正确的。

做这道题关键在于理解题意。这道题让我想起了流水作业调度的Johnson算法,虽然与这道题无关。

提示几点

  1. 每个工件的每个工序对应着不同的机器。
  2. 一个工件必须按照加工顺序加工。
  3. 把每个机器看成一个时间轴,每个时间对应着加工一个工件,或者为空闲状态。
  4. 题中的算法是给定的贪心策略,不需要构造,只要模拟。

由于数据很小,把每个机器的时间轴用布尔数组表示,true为该时间有工件在加工,false为空闲。

按照给定的顺序,一件一件得往时间轴上插入,每个工件插入的位置必须在前面的工序都完成以后的时间断插入。每次插入扫描一遍时间轴数组,找到最前面一个。

其实题中所给的图已经一目了然了。理解题意以后,会发现这是NOIP2006最简单的题。

滑雪场评级

http://www.cnblogs.com/jznoi/p/4071616.html

难点提要:1 思想(最小生成树的一个性质) 2 并查集的维护 

1 思想:

   将所有点标号,并将高度差绝对值作为边长。

   先来想这样一个问题:

    要使得一棵t个节点的树,max{边权} 最小 即使得任意两点间路径上的max{边权}最小

    最小生成树中 任意两点间路径上的max{边权}是最小是a的。为什么?

         反证 假设有一条更小的边b 加进生成树 必形成一环 在做最小生成树的时候 是按边权从小到大加入 

         肯定先加入 b再加a的时候 判环 a不会加入 与假设矛盾。

  回到原题

      按边权从小到大加入 如果一个连通块有t个数了 那么 这个连通块里所有的起点的D都为新加进来的边权

    如果 某个两个连通块要合并

            其中一个连通块已经>=t了 那么 这个连通块中都不用再走了 (不可能被更新)

            如果另一个<t 那么 走哇 否则 也不走哇。

  o(n*m*2条边+dfs的时间复杂度(每个点只走一次,n*m))

  那么

function find(x:int64):int64;

begin
if x=fa[x] then exit(x);
fa[x]:=find(fa[x]);
exit(fa[x]);
end;
function su(x,y:longint):int64;
begin
exit((x-1)*m+y);
end;
procedure build(x,y,x1,y1:int64);
begin
inc(tot);
a[tot].x:=su(x,y);
a[tot].y:=su(x1,y1);
a[tot].w:=abs(b[x,y]-b[x1,y1]);
end;
procedure dfs(x,w:int64);
var i,y:longint;
begin
flag[x]:=true;

{dfs走的图必定是未去过的新图,所以要用flag}

if f[x] then begin inc(ans,w); f[x]:=false; end;
for i:=1 to h[x,0] do
begin
y:=h[x,i];
if not flag[y] then dfs(y,w);
end;
end;
begin
readln(n,m,t);
for i:=1 to n do
begin
for j:=1 to m do read(b[i,j]);
readln;
end;
for i:=1 to n do
begin
for j:=1 to m do
begin
read(x);
if x=1 then f[su(i,j)]:=true;
end;
readln;
end;
for i:=1 to n do
for j:=1 to m do
begin
if i<n then build(i,j,i+1,j);
if j<m then build(i,j,i,j+1);{标号建原图}
end;
for i:=1 to n*m do begin size[i]:=1; fa[i]:=i; end;
sort(1,tot);
i:=0; num:=0;
while (i<tot) and (num<n*m-1) do
begin
inc(i);
set1:=find(a[i].x); x:=a[i].x; y:=a[i].y;
set2:=find(a[i].y);
w:=a[i].w;
if set1=set2 then continue;
inc(num);
if (size[set1]<t) and (size[set1]+size[set2]>=t) then dfs(set1,w);{只有未赋过值的连通块才要赋值}
if (size[set2]<t) and (size[set1]+size[set2]>=t) then dfs(set2,w);
if size[set1]<size[set2] then begin k:=set1; set1:=set2; set2:=k; end;

//安置合并,使得size更大的连通块深度跟小 

{一个size为500 另一个 2  如果 2的father作为500的father ,搜500连通块里数的father要多搜500次哦}
size[set1]:=size[set1]+size[set2];
fa[set2]:=set1;
inc(h[x,0]); h[x,h[x,0]]:=y;{新图,其实如果连通块已经在以前的循环中赋过值了,不必去走,也不要赋边了呀}
inc(h[y,0]); h[y,h[y,0]]:=x;[-1..n*m,-1..10]最多十条边呀
end;
writeln(ans);
end.

队伍平衡

http://www.cnblogs.com/jznoi/p/4072697.html

搜索也有好换之分 尽量使层数减小 即 次数减少 底数增大 更优

  枚举 人在哪个组 4的12次

  枚举 哪个组第几个位置是哪个人 12的12次

滑雪录像

http://www.cnblogs.com/jznoi/p/4072697.html

贪心 按右边界排序 加入两个队列

加入原则:

  假如两个队列都能加,加原队列右边界更远的

 -----|  ---|

---|  

还是

-----|  

---|     ---|

对于这种情况无影响

-----|  ---|

---|      -----|

-----|   -----|

---|     ---|

但是

-----|  ---|

---|  --------|

-----|--------|(重合了)

---|     ---|

按右边界加入的证明 

先看一条队列 两条队列也是一样

贪心即为   

 ----   -----1    ----

 ----       ------2 --- 

应该选谁,如果1 或2 与之前边界重合 那当然选 不重合的

  如果两条不重合 那么 选1 之后选进来的数量只增不减

因为需要尽量多的独立的线段,所以每个线段都尽可能的小,

对于同一右端点,左端点越大,线段长度越小。

那么为什么要对右端点进行排序呢?

如果左端点进行排序,那么右端点是多少并不知道,那么每一条线段都不能对之前所有的线段进行一个总结,那么这就明显

不满足贪心的最优结构了。

滑雪场建设

http://www.cnblogs.com/jznoi/p/4072697.html

难点:思路

每次找到一个最大的正方形 最大的正方形必定是最后一步覆盖的 而前一步 这个正方形中可以为任意数,将它赋为任意数(-1)重复步骤 

而边长更大的正方形必能被边长更小的正方形完全覆盖。反之 不成立。

ans:=min{ 最大正方形边长} bytheway用动归或二分求最大的正方形。

还有一点 当整个长方形全相同或最大正方形=1时退出

旅行家的预算

https://vijos.org/p/1253

n=0特判 

如果 存在两油站之间距离超过加满一次油所需时间 -1

走 找到在当前油站k加满一次油 能走到 油价p[i]最便宜的油站i

  if p[i]>p[k] 那么在当前油站加满油再走 else 加油,走到那个油站刚好停下的油

守望者的逃离

https://vijos.org/p/1431

一个s记录只用魔法能走到的距离

另一个smax记录用最远能走到的距离

因为守望者跑步、闪烁或休息活动均以秒(s)为单位,且每次活动的持续时间为整数秒。

如果能用魔法就必定用,用一次1s 走路一次也是一秒。 如果无法恢复 就只能用走。。

代码还是有点巧妙

   d:=0; dmax:=0;

  for i:=1 to t do

原文地址:https://www.cnblogs.com/thedevil/p/4077165.html