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
",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
",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
",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
",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
",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
",ans);
102     return 0;
103 }
网络流做法


原文地址:https://www.cnblogs.com/WNJXYK/p/4064083.html