• 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 }
    网络流做法


  • 相关阅读:
    electron 文件打包取反
    vue自定义组件实现vmodel(含vue3) 飞鸟和蝉
    CSS_flex布局解决最后一行分散问题 飞鸟和蝉
    Vue3 使用Pinia状态管理 飞鸟和蝉
    自定义inputfile样式,并实现nodejs接收并返回
    vue3 给dom添加方法
    vite+vite3动态添加组件
    flex 排版问题
    java和js 用正则表达式切割省市县的代码
    100多个常用 API 接口整理大全
  • 原文地址:https://www.cnblogs.com/WNJXYK/p/4064083.html
Copyright © 2020-2023  润新知