10.30 NFLS-NOIP模拟赛 解题报告

总结:今天去了NOIP模拟赛,其实是几道USACO的经典的题目,第一题和最后一题都有思路,第二题是我一开始写了个spfa,写了一半中途发现应该是矩阵乘法,然后没做完,然后就没有然后了!第二题的暴力都没码QAQ 现在我来写解题报告了,有点饿了QAQ、、

 


第一题


题目 1: 架设电话线 [Jeffrey Wang, 2007]



    最近,Farmer John的奶牛们越来越不满于牛棚里一塌糊涂的电话服务,于

是,她们要求FJ把那些老旧的电话线换成性能更好的新电话线。新的电话线架设

在已有的N(2 <= N <= 100,000)根电话线杆上,第i根电话线杆的高度为

height_i米(1 <= height_i <= 100)。电话线总是从一根电话线杆的顶端被引到

相邻的那根的顶端,如果这两根电话线杆的高度不同,那么FJ就必须为此支付

C*电话线杆高度差(1 <= C <= 100)的费用。当然,你不能移动电话线杆,只能

按原有的顺序在相邻杆间架设电话线。



    Farmer John认为,加高某些电话线杆能减少架设电话线的总花费,尽管这

项工作也需要支出一定的费用。更准确地,如果他把一根电话线杆加高X米的话

,他得为此付出X^2的费用。



    请你帮Farmer John计算一下,如果合理地进行这两种工作,他最少要在这

个电话线改造工程上花多少钱。



程序名: telewire



输入格式:



* 第1行: 2个用空格隔开的整数:N和C



* 第2..N+1行: 第i+1行仅有一个整数:height_i



输入样例 (telewire.in):



5 2

2

3

5

1

4



输入说明:



    一共有5根电话线杆,在杆间拉电话线的费用是每米高度差$2。在改造之前

,电话线杆的高度依次为2,3,5,1,4米。



输出样例:



* 第1行: 输出Farmer John完成电话线改造工程所需要的最小花费



输出样例 (telewire.out):



15



输出说明:



    最好的改造方法是:Farmer John把第一根电话线杆加高1米,把第四根加

高

2米,使得它们的高度依次为3,3,5,3,4米。这样花在加高电线杆上的钱是

$5

。此时,拉电话线的费用为$2*(0+2+2+1) = $10,总花费为$15。

题解


一开始考场上看到这题我惊喜做过的,然后发现呵呵,原来USACO有两题架设电话线,不过没关系感觉这道题DP能水一点分数,我就一开始顺手码了个暴力的DP,最后是90分,TLE了一个点。【P.S点我去,我回家i7四核都跑不出90分,这成绩真的没问题?】我暴力DP的思路就是f[i][j]表示第i个电线杆高度为j时,此电线杆和之前所有电线杆还有架线的最小花费。这样时间复杂度是O(nh^2)。从估计来看,欸?明明应该是水过啊!好吧,复杂度多了一个0,水过个P啊!好吧,来讲一讲正解,正解的复杂度是O(nh)的,这样刚好比超时少一个0.f[i][j]表示前i个,为j的个数,f[i][j]=f[i-1][p]+abs(p-j)*c+(j-a[i])*(j-a[i]);然后记录两个数组low,high,转移。

 


 

代码


 1 /*ZZ*/

 2 #include<cstdio>

 3 #include<cstring>

 4 using namespace std;

 5 const int Maxn=100000;

 6 const int Maxh=100;

 7 int n,c;

 8 int hi[Maxn+10];

 9 int f[Maxn+10][Maxh+10];

10 inline int remin(int a,int b){

11     if (a<b) return a;

12     return b;

13 }

14 inline int abs(int x){

15     if (x<0) return -x;

16     return x;

17 }

18 int main(){

19     freopen("telewire.in","r",stdin);

20     freopen("telewire.out","w",stdout);

21     scanf("%d%d",&n,&c);

22     for (int i=1;i<=n;i++) scanf("%d",&hi[i]);

23     memset(f,127,sizeof(f));

24     for (int i=hi[1];i<=Maxh;i++) f[1][i]=(i-hi[1])*(i-hi[1]);

25     for (int i=2;i<=n;i++){

26         for (int h=hi[i];h<=Maxh;h++){

27             for (int k=Maxh;k>=1;k--){

28                 if (f[i-1][k]==f[0][0]) break;

29                 f[i][h]=remin(f[i][h],f[i-1][k]+abs(k-h)*c+(h-hi[i])*(h-hi[i]));

30             }

31         }

32     }

33     int Ans=2147483647;

34     for (int h=hi[n];h<=Maxh;h++){

35         Ans=remin(Ans,f[n][h]);

36     }

37     printf("%d\n",Ans);

38     return 0;

39 }
90分代码
 1 #include<iostream>

 2 #include<cstdio>

 3 #include<cstring>

 4 using namespace std;

 5 int f[1005];

 6 int dp[1005];

 7 int a[100005];

 8 int low[1005];

 9 int high[1005];

10 int c;

11 int n;

12 int p;

13 int ans=-1;

14 int abs(int x)

15 {

16     if(x>0)return x;

17     return -x;

18 }

19 int sum;

20 int main()

21 {

22     freopen("telewire.in","r",stdin);

23     freopen("telewire.out","w",stdout);

24     scanf("%d%d",&n,&c);

25     for(int i=1;i<=n;i++)

26     {

27        scanf("%d",&a[i]);

28        p=max(p,a[i]);

29        if(i>1)sum+=c*abs(a[i]-a[i-1]);

30     }

31     sum=sum+5;

32     for(int i=0;i<a[1];i++)

33     dp[i]=sum;

34     for(int i=a[1];i<=p;i++)

35     dp[i]=(i-a[1])*(i-a[1]);

36     for(int i=2;i<=n;i++)

37     {

38         for(int j=0;j<=100;j++)

39         {

40             f[j]=dp[j];

41             dp[j]=low[j]=high[j]=sum;

42         }

43         low[0]=f[0];

44         for(int j=1;j<=100;j++)

45         {

46             low[j]=min(low[j-1],f[j]-c*j);

47         }

48         high[100]=f[100]+100*c;

49         for(int j=99;j>=0;j--)

50         {

51             high[j]=min(high[j+1],f[j]+c*j);

52         }

53         for(int j=a[i];j<=100;j++)

54         {

55             dp[j]=min(high[j]-c*j,low[j]+c*j)+(j-a[i])*(j-a[i]);

56         //    cout<<i<<" "<<j<<" "<<dp[j]<<endl;

57         }

58     }

59     for(int i=a[n];i<=100;i++)

60     if(ans==-1 || dp[i]<ans)ans=dp[i];

61     printf("%d\n",ans);

62     return 0;

63 }
100分代码

 


第二题


题目 2: 奶牛接力 [Erik Bernhardsson, 2003]



    FJ的N(2 <= N <= 1,000,000)头奶牛选择了接力跑作为她们的日常锻炼项目

。至于进行接力跑的地点,自然是在牧场中现有的T(2 <= T <= 100)条跑道

上。 



    农场上的跑道有一些交汇点,每条跑道都连结了两个不同的交汇点I1_i和

I2_i(1 <= I1_i <= 1,000; 1 <= I2_i <= 1,000)。每个交汇点都是至少两条跑

道的端点。奶牛们知道每条跑道的长度length_i(1 <= length_i <= 1,000),以

及每条跑道连结的交汇点的编号。并且,没有哪两个交汇点由两条不同的跑道直

接相连。你可以认为这些交汇点和跑道构成了一张图。



    为了完成一场接力跑,所有N头奶牛在跑步开始之前都要站在某个交汇点上

(有些交汇点上可能站着不只1头奶牛)。当然,她们的站位要保证她们能够将

接力棒顺次传递,并且最后持棒的奶牛要停在预设的终点。



    你的任务是,写一个程序,计算在接力跑的起点(S)和终点(E)确定的情况下

,奶牛们跑步路径可能的最小总长度。显然,这条路径必须恰好经过N条跑道。



程序名: relays



输入格式:



* 第1行: 4个用空格隔开的整数:N,T,S,以及E



* 第2..T+1行: 第i+1为3个以空格隔开的整数:length_i,I1_i,以及I2_i,

描

              述了第i条跑道。



输入样例 (relays.in):



2 6 6 4

11 4 6

4 4 8

8 4 9

6 6 8

2 6 9

3 8 9



输出格式:



* 第1行: 输出1个正整数,表示起点为S、终点为E,并且恰好经过N条跑道的路

         径的最小长度



输出样例 (relays.out):



10

题解


这道题我当时真的是一点思路都没有啊,感觉只能搜索,但是点那么多边只有100真的很可疑啊!我先写了一个spfa,然后感觉不太对,又写了矩阵乘法!其实应该是正解了,但是我没调完就结束了QAQ,我就再次贴上网上的一个矩阵乘法的思路:题目求i,j之间边数恰为N的最短路径(边可以重复走),我们知道线性代数中有:01邻接矩阵A的K次方C=A^K,C[i][j]表示i点到j点正好经过K条边的路径数。而floyd则是每次使用一个中间点k去更新i,j之间的距离,那么更新成功表示i,j之间恰有一个点k时的最短路,如果做N次floyd那么不就是i,j之间借助N个点时的最短路了?考虑当a[i][k]+a[k][j]<c[i][j]的时候,c[i][j]=a[i][k]+a[k][j],这样c[i][j]保存了i,j之间有一个点的最短路,第二次将c[i][j]拷贝回到a[i][j]当中,并将c[i][j]重新置为INF,再做一次,则是在原来的基础上在i,j之间再用一个点k来松弛,这时候i,j之间实际上已经是两个点了,之后重复这么做就好了,可以利用二进制加速。其实,我们当时考试有人用暴力瞎搞搞也就弄出来了QAQ这、、、、、赫赫!

 


 

代码


 1 #include <cstdio>

 2 #include <cstring>

 3 #include <algorithm>

 4 #include <set>

 5 using namespace std ;

 6 typedef long long LL ;

 7 const double eps(1e-10) ;

 8 const int inf(0x3f3f3f3f) ;

 9 int n,t,s,e;

10 struct N{

11     int x,y,z;

12 }a[105];

13 int to[1005];

14 set<int> S;

15 int siz;

16 int mul[205][205];

17 int map[205][205];

18 void p(int a[][205],int b[][205]){

19     static int c[205][205];

20     memset(c,0x3f,sizeof c);

21     for(int i=1;i<=siz;i++){

22         for(int j=1;j<=siz;j++){

23             for(int k=1;k<=siz;k++){

24                 c[i][j]=min(a[i][k]+b[k][j],c[i][j]);

25             }

26         }

27     }

28     memcpy(a,c,sizeof map);

29 }

30 int main(){

31     freopen("relays.in","r",stdin) ;

32     freopen("relays.out","w",stdout) ;

33     scanf("%d%d%d%d",&n,&t,&s,&e);

34     S.insert(s);S.insert(e);

35     for(int i=0;i<t;i++){

36         scanf("%d%d%d",&a[i].z,&a[i].x,&a[i].y);

37         S.insert(a[i].x);S.insert(a[i].y);

38     }

39     for(set<int>::iterator it=S.begin();it!=S.end();++it){

40         to[*it]=++siz;

41     }

42     memset(map,0x3f,sizeof map);

43     for(int i=0;i<t;i++){

44         map[to[a[i].y]][to[a[i].x]]=map[to[a[i].x]][to[a[i].y]]=min(map[to[a[i].x]][to[a[i].y]],a[i].z);

45     }

46     memcpy(mul,map,sizeof map);

47     int k=n-1;

48     while(k){

49         if(k&1) p(map,mul);

50         p(mul,mul);

51         k>>=1;

52     }

53     printf("%d\n",map[to[s]][to[e]]);

54     return 0 ;

55 }
矩阵乘法
 1 #include<cstdio>

 2 #include<cstring>

 3 #include<algorithm>

 4 using namespace std;

 5 int f[2][10005];

 6 struct node

 7 {

 8     int len;

 9     int x;

10     int y;

11 }g[10005];

12 const int inf=2e9+7;

13 struct srt

14 {

15     int val;

16     int id;

17 }sr[10005];

18 int cnt,used[10005];

19 int final[10005];

20 int main()

21 {

22     freopen("relays.in","r",stdin);

23     freopen("relays.out","w",stdout);

24     int n,m,s,t;

25     scanf("%d%d%d%d",&n,&m,&s,&t);

26     for(int i=1;i<=m;i++) 

27     {

28         scanf("%d%d%d",&g[i].len,&g[i].x,&g[i].y);

29         used[g[i].x]=1;

30         used[g[i].y]=1;

31     }

32     for(int i=1;i<=1000;i++)

33     {

34         if(used[i])

35         {

36             sr[cnt].val=i;

37             sr[cnt].id=cnt;

38             cnt++;

39         }

40     }

41     for(int i=1;i<=cnt;i++) final[sr[i].val]=sr[i].id;

42     for(int i=1;i<=m;i++)

43     {

44         g[i].x=final[g[i].x]+1;

45         g[i].y=final[g[i].y]+1;

46     }

47     int cur=1;

48     s=final[s]+1;

49     t=final[t]+1;

50     for(int i=1;i<=m;i++) f[0][i]=f[1][i]=inf;

51     f[0][s]=0;

52     //离散化

53     for(int i=1;i<=n;i++)

54     {

55         int pre=1-cur;

56         for(int j=1;j<=m;j++)

57         {

58             int start=g[j].x,end=g[j].y,dis=g[j].len;

59             if(f[pre][start]+dis<f[cur][end]) f[cur][end]=f[pre][start]+dis;

60             if(f[pre][end]+dis<f[cur][start]) f[cur][start]=f[pre][end]+dis;

61         }

62         cur=1-cur;

63         for(int j=1;j<=m;j++) f[pre][j]=inf;

64     }

65     printf("%d\n",f[1-cur][t]);

66     //暴力

67     return 0;

68 }
某暴力瞎搞搞

 


第三题


题目 3: 分配防晒霜 [Russ Cox, 2001]



    奶牛们计划着去海滩上享受日光浴。为了避免皮肤被阳光灼伤,所有C(1 <= 

C <= 2500)头奶牛必须在出门之前在身上抹防晒霜。第i头奶牛适合的最小和最

大的SPF值分别为minSPF_i和maxSPF_i(1 <= minSPF_i <= 1,000; minSPF_i <= 

maxSPF_i <= 1,000)。如果某头奶牛涂的防晒霜的SPF值过小,那么阳光仍然能

把她的皮肤灼伤;如果防晒霜的SPF值过大,则会使日光浴与躺在屋里睡觉变得

几乎没有差别。



    为此,奶牛们准备了一大篮子防晒霜,一共L(1 <= L <= 2500)瓶。第i瓶

防

晒霜的SPF值为SPF_i(1 <= SPF_i <= 1,000)。瓶子的大小也不一定相同,第i

瓶

防晒霜可供cover_i头奶牛使用。当然,每头奶牛只能涂某一个瓶子里的防晒霜

,而不能把若干个瓶里的混合着用。



    请你计算一下,如果使用奶牛们准备的防晒霜,最多有多少奶牛能在不被灼

伤的前提下,享受到日光浴的效果?



程序名: tanning



输入格式:



* 第1行: 2个用空格隔开的整数:C和L



* 第2..C+1行: 第i+1行给出了适合第i头奶牛的SPF值的范围:minSPF_i以及

              maxSPF_i



* 第C+2..C+L+1行: 第i+C+1行为了第i瓶防晒霜的参数:SPF_i和cover_i,两个

                  数间用空格隔开。



输入样例 (tanning.in):



3 2

3 10

2 5

1 5

6 2

4 1



输入说明:



    一共有3头奶牛,2瓶防晒霜。3头奶牛适应的SPF值分别为3..10,2..5,以

及1..5。2瓶防晒霜的SPF值分别为6(可使用2次)和4(可使用1次)。可能的分

配方案为:奶牛1使用第1瓶防晒霜,奶牛2或奶牛3使用第2瓶防晒霜。显然,最

多只有2头奶牛的需求能被满足。



输出格式:



* 第1行: 输出1个整数,表示最多有多少头奶牛能享受到日光浴



输出样例 (tanning.out):



2

题解


这道题我用贪心A掉了,相对两个maxSPF一样的牛cow1与cow2,若cow1的minSPF<cow2的minSPF,那么对于一瓶cow1能用的防晒霜,cow2也能用,因为对于答案的贡献都是1,所以给谁都可以,但是若cow2能用的防晒霜cow1未必能用,所以cow1能用应优先给cow1.所以对于所有的牛maxSPF第一关键字升序,minSPF第二关键字降序,然后防晒霜升序,能给就给,贪心就好!考场里还有人用了别的做法->网络流,这个建图是十分显而易见的,复杂度也完全能过。【NOIP模拟赛你们确定要用网络流虐?】


 

代码


 1 /*ZZ*/

 2 #include<cstdio>

 3 #include<algorithm>

 4 using namespace std;

 5 int n,l;

 6 struct Crow{

 7     int max;

 8     int min;

 9 };

10 Crow c[2505];

11 struct Spf{

12     int spf;

13     int num;

14 };

15 Spf s[2505];

16 inline bool cmp(Crow a,Crow b){

17     if (a.max<b.max) return true;

18     if (a.max==b.max && a.min>b.min) return true;

19     return false;

20 }

21 int main(){

22     freopen("tanning.in","r",stdin);

23     freopen("tanning.out","w",stdout);

24     scanf("%d%d",&n,&l);

25     for (int i=1;i<=n;i++) scanf("%d%d",&c[i].min,&c[i].max);

26     for (int i=1;i<=l;i++) scanf("%d%d",&s[i].spf,&s[i].num);

27     sort(c+1,c+n+1,cmp);

28     int index;

29     int Ans=0;

30     for (int i=1;i<=n;i++){

31         index=-1;

32         for (int j=1;j<=l;j++){

33             if (s[j].num>0 && c[i].min<=s[j].spf && s[j].spf<=c[i].max){

34                 if (index==-1){

35                     index=j;

36                 }else{

37                     if (s[j].spf<s[index].spf) index=j;

38                 }

39             }

40         }

41         if (index!=-1){

42             Ans++;

43             s[index].num--;

44         }

45     }

46     printf("%d\n",Ans);

47     return 0;

48 } 
贪心做法
  1 #include<iostream>

  2 #include<cstdio>

  3 #include<queue>

  4 #include<vector>

  5 #define inf 19991231 

  6 using namespace std;

  7 int n,l;

  8 int s,t;

  9 int ans=0;

 10 int res;

 11 int f[10005];

 12 int a[10005];

 13 int b1[10005],b2[10005];

 14 struct node

 15 {

 16     int val;

 17     int opp;

 18     int to;

 19 };

 20 vector <node> v[10005];

 21 int d[10050];

 22 void add(int x,int y,int val)

 23 {

 24     node tmp;

 25     tmp.to=y;tmp.val=val;tmp.opp=v[y].size();

 26     v[x].push_back(tmp);

 27     tmp.to=x;tmp.val=0;tmp.opp=v[x].size()-1;

 28     v[y].push_back(tmp);

 29 }

 30 bool make_level()

 31 {

 32     for(int i=1;i<=t;i++)d[i]=-1;

 33     d[0]=0;

 34     queue <int> q;

 35     q.push(0);

 36     while(!q.empty())

 37     {

 38         int x=q.front();

 39         q.pop();

 40         for(int i=0;i<v[x].size();i++)

 41         {

 42             if(d[v[x][i].to]==-1 && v[x][i].val>0)

 43             {

 44                 d[v[x][i].to]=d[x]+1;

 45                 q.push(v[x][i].to);

 46             }

 47         }

 48     }

 49     return d[t]!=-1;

 50 }

 51 int dfs(int x,int cap)

 52 {

 53     if(x==t)return cap;

 54     int r=0;

 55     for(int i=0;i<v[x].size();i++)

 56     {

 57         if(d[v[x][i].to]==d[x]+1 && v[x][i].val>0)

 58         {

 59             int what=min(v[x][i].val,cap-r);

 60             what=dfs(v[x][i].to,what);

 61             r+=what;

 62             v[x][i].val-=what;

 63             v[v[x][i].to][v[x][i].opp].val+=what;

 64         }

 65     }

 66     if(!r)d[x]=-2;

 67     return r;

 68 }

 69 int main()

 70 {

 71     freopen("tanning.in","r",stdin);

 72     freopen("tanning.out","w",stdout);

 73     scanf("%d%d",&n,&l);

 74     s=0;

 75     t=n+l+1;

 76     for(int i=1;i<=n;i++)

 77     {

 78          scanf("%d%d",&b1[i],&b2[i]);

 79          add(l+i,t,1);

 80      }

 81     for(int i=1;i<=l;i++)

 82     {

 83         int x;

 84         scanf("%d%d",&a[i],&x);

 85         add(s,i,x);

 86     }

 87     for(int i=1;i<=l;i++)

 88     {

 89         for(int j=1;j<=n;j++)

 90         {

 91             if(a[i]>=b1[j] && a[i]<=b2[j])

 92             {

 93                 add(i,j+l,1);

 94             }

 95         }

 96     }

 97     while(make_level())

 98     {

 99         ans+=dfs(s,inf);

100     }

101     printf("%d\n",ans);

102     return 0;

103 }
网络流做法

 


你可能感兴趣的:(IP)