zoukankan      html  css  js  c++  java
  • Codeforces Round #277.5 (Div. 2)

    比赛页面:http://codeforces.com/contest/489

    官方题解:http://codeforces.com/blog/entry/14741

    A. SwapSort

    给n个数,要求按从小到大排序并且交换次数不超过n,输出一个交换方案。

    排序,交换次数最少的是选择排序,交换次数最多是n,正好满足题意

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cmath>
     4 #include<iostream>
     5 #include<algorithm>
     6 #include<set>
     7 #include<map>
     8 #include<stack>
     9 #include<vector>
    10 #include<queue>
    11 #include<string>
    12 #include<sstream>
    13 #define eps 0.000001
    14 #define ALL(x) x.begin(),x.end()
    15 #define INS(x) inserter(x,x.begin())
    16 using namespace std;
    17 typedef long long LL;
    18 int i,j,k,n,m,x,y,T,big,cas,a[30005],ans[30005][2];
    19 bool flag;
    20 int main()
    21 {
    22     scanf("%d",&n);
    23     for (int i=0;i<n;i++)
    24     {
    25         scanf("%d",&a[i]);
    26     }
    27     for (i=0;i<n;i++)
    28     {
    29         k=i;
    30         for (j=i+1;j<n;j++)
    31         {
    32             if (a[k]>a[j]) k=j;
    33         }
    34         if (k!=i)
    35         {
    36             swap(a[k],a[i]);
    37             ans[m][0]=i;
    38             ans[m][1]=k;
    39             m++;
    40         }
    41     }
    42     printf("%d
    ",m);
    43     for (int i=0;i<m;i++)
    44     {
    45         printf("%d %d
    ",ans[i][0],ans[i][1]);
    46     }
    47     
    48     return 0;
    49 }
    View Code

    B. BerSU Ball

    n个男孩和m个女孩要跳舞,每个人都有dancing skill值,并且要男女配对的话dancing skill的差不能超过1。问最大能匹配多少对。

    贪心,猛地一看还以为是二分图,但是如果将男孩的dancing skill和女孩的dancing skill分别排序的话,能与每个男孩匹配的女孩是一个连续的区间,这样就可以用贪心算法,即按照dancing skill从小到大扫描每个男孩,每个男孩尽量匹配dancing skill小的女孩。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cmath>
     4 #include<iostream>
     5 #include<algorithm>
     6 #include<set>
     7 #include<map>
     8 #include<stack>
     9 #include<vector>
    10 #include<queue>
    11 #include<string>
    12 #include<sstream>
    13 #define eps 0.000001
    14 #define ALL(x) x.begin(),x.end()
    15 #define INS(x) inserter(x,x.begin())
    16 using namespace std;
    17 typedef long long LL;
    18 int i,j,k,n,m,x,y,T,ans,big,cas,a[205],b[205];
    19 bool flag;
    20 int main()
    21 {
    22     scanf("%d",&n);
    23     for (i=1;i<=n;i++)
    24     {
    25         scanf("%d",&a[i]);
    26     }
    27     scanf("%d",&m);
    28     for (i=1;i<=m;i++)
    29     {
    30         scanf("%d",&b[i]);
    31     }
    32     sort(a+1,a+1+n);
    33     sort(b+1,b+1+m);
    34     for (i=1;i<=n;i++)
    35     {
    36         for (j=1;j<=m;j++)
    37         {
    38             if (b[j]<0) continue;
    39             if (abs(a[i]-b[j])<=1)
    40             {
    41                 ans++;
    42                 b[j]=-1;
    43                 break;
    44             }
    45         }
    46     }
    47     printf("%d
    ",ans);
    48     return 0;
    49 }
    View Code

    C. Given Length and Sum of Digits...

    给出m和s,要求一个数,这个数有m位,并且所有位的和为s,输出这个数最小可能值以及最大可能值。

    分类讨论,记得m=0并且s>1的时候没有解,s>m*9的时候也没有解。接下来先将s分出1赋给最高位,然后求最小值的话依次从低位向最高位填数,求最大值就依次从最高位向最低位填数。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cmath>
     4 #include<iostream>
     5 #include<algorithm>
     6 #include<set>
     7 #include<map>
     8 #include<stack>
     9 #include<vector>
    10 #include<queue>
    11 #include<string>
    12 #include<sstream>
    13 #define eps 0.000001
    14 #define ALL(x) x.begin(),x.end()
    15 #define INS(x) inserter(x,x.begin())
    16 using namespace std;
    17 typedef long long LL;
    18 int i,j,k,n,m,x,y,T,ans[105],big,cas,s,ss;
    19 bool flag;
    20 int main()
    21 {
    22     cin>>m>>s;
    23     if (s==0)
    24     {
    25         if (m==1)
    26         {
    27             printf("0 0
    ");
    28             return 0;
    29         }else
    30         {
    31             printf("-1 -1
    ");
    32             return 0;
    33         }
    34     }
    35     if (s>m*9)
    36     {
    37         printf("-1 -1
    ");
    38         return 0;
    39     }
    40     ss=s;
    41     ans[m]=1;s--;
    42     for (i=1;i<=m;i++)
    43     {
    44         if (s>9)
    45         {
    46             ans[i]+=9;
    47             s-=9;
    48         }else
    49         {
    50             ans[i]+=s;
    51             break;
    52         }
    53     }
    54     for (i=m;i>=1;i--) printf("%d",ans[i]);
    55     printf(" ");
    56     memset(ans,0,sizeof(ans));
    57     s=ss;
    58     ans[1]=1;s--;
    59     for (i=1;i<=m;i++)
    60     {
    61         int temp=9-ans[i];
    62         if (s>temp)
    63         {
    64             ans[i]+=temp;
    65             s-=temp;
    66         }else
    67         {
    68             ans[i]+=s;
    69             break;
    70         }
    71     }
    72     for (i=1;i<=m;i++) printf("%d",ans[i]);
    73     printf("
    ");
    74         
    75     return 0;
    76 }
    View Code

    D. Unbearable Controversy of Being

    给出一个有向图,n为结点数,m为边数,我们要找damn rhombus的个数。damn rhombus是指,结点a,b,c,d,a->b->c,a->d->c,这样的一个图形。

    图论,我们开一个3000*3000的数组edge,edge[a][c]表示由结点a只经过一个中间结点到结点c的路径数,依次枚举每一个结点为中间结点即可。如果找到一个a->b->c的路径,那么可以与之前找到的从a到c只经过一个结点的路径组成damn rhombus(即edge[a][c]中的值),累加到ans中再更新edge[a][c]。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cmath>
     4 #include<iostream>
     5 #include<algorithm>
     6 #include<set>
     7 #include<map>
     8 #include<stack>
     9 #include<vector>
    10 #include<queue>
    11 #include<string>
    12 #include<sstream>
    13 #define eps 0.000001
    14 #define ALL(x) x.begin(),x.end()
    15 #define INS(x) inserter(x,x.begin())
    16 using namespace std;
    17 typedef long long LL;
    18 int i,j,k,n,m,x,y,T,ans,big,cas,p,q;
    19 bool flag;
    20 vector <int> ru[3005],chu[3005];
    21 int edge[3005][3005];
    22 int main()
    23 {
    24     scanf("%d%d",&n,&m);
    25     for (i=1;i<=m;i++)
    26     {
    27         scanf("%d%d",&x,&y);
    28         chu[x].push_back(y);
    29         ru[y].push_back(x);
    30     }
    31     for (i=1;i<=n;i++)
    32     {
    33         for (j=0;j<chu[i].size();j++)
    34         {
    35             for (k=0;k<ru[i].size();k++)
    36             {
    37                 p=chu[i][j];q=ru[i][k];
    38                 if (p==q) continue;
    39                 ans+=edge[p][q];
    40                 edge[p][q]++;
    41             }
    42         }
    43     }
    44     printf("%d
    ",ans);
    45     return 0;
    46 }
    View Code

    E. Hiking

    一条直线,从0开始旅行,中间有n个休息点,每个休息点有坐标xi,和风景值bi,旅行者想要每天旅行路程l,然后住到一个休息点中,由于两个休息点间很难找到恰好相距为l的,因此旅行者会产生失望值,其中rj为当天行走的路程。求最小的相对失望值(失望值除以风景值的和)

    二分答案 + DP (01分数规划)

    我一开始想的是单纯的DP,每到一个休息点就求到这个休息点的最小相对失望值,但发现这样做有后效性(比如分子分母都加一,2/3 -> 3/4 与 5/9 -> 6/10显然前者求解的值更小,但是不一定取前一个状态的最小值)

    最后参考了这位同学的题解,二分答案。

    不妨设每天产生的失望值 si=,sigema(si)/sigema(bi)=ans,设f[ans]=sigema(si)-ans*sigema(bi),f[ans]可以化简为 f[ans]=sigema(si-ans*bi) 

    显然如果要选取的si和bi都确定了,则f[ans]随ans增大而减小。

    我们需要找到一个ans,使得存在一组解使得f[ans]=0,并且对于其他的任意组解都有f[ans]>=0(这样的ans是最小值)。因此可以二分ans,对每得到的一个ans=mid用DP求出f[mid]的最小值,如果f[mid]<0,说明 sigema(si)/sigema(bi)<mid,有比mid更优的解,这个解比mid更小,因此继续在区间[l,mid]中寻找解,否则,在区间[mid,r]中寻找。

    这篇博文说的更详细一些http://www.cnblogs.com/zhyfzy/p/4113198.html

    顺带一提,这道题的精度误差eps应当设置的小一些,比如1e-9,因为精度WA了1发然后找了接近一个小时的错误上概率论课还迟到了也是醉了

     1 #include<bits/stdc++.h>
     2 #define eps 1e-9
     3 #define FOR(i,j,k) for(int i=j;i<=k;i++)
     4 using namespace std;
     5 typedef long long LL;
     6 int i,j,k,n,m,y,T,ans,big,cas,pre[1005],x[1005],b[1005],len;
     7 bool flag;
     8 double l,r,mid,dp[1005];
     9 double run(double u)
    10 {
    11     double temp;
    12     memset(pre,0,sizeof(pre));
    13     for (i=1;i<=n;i++)
    14     {
    15         dp[i]=1e10;
    16         for (j=0;j<i;j++)
    17         {
    18             temp=dp[j]+sqrt(abs(x[i]-x[j]-len))-u*b[i];
    19             if (temp<dp[i])
    20             {
    21                 dp[i]=temp;
    22                 pre[i]=j;
    23             }
    24         }
    25     }
    26     return dp[n];
    27 }
    28 
    29 void output(int x)
    30 {
    31     if (pre[x]) output(pre[x]);
    32     if (x==n) printf("%d
    ",x);
    33     else printf("%d ",x);
    34 }
    35 
    36 int main()
    37 {
    38     scanf("%d%d",&n,&len);
    39     for (i=1;i<=n;i++)
    40     {
    41         scanf("%d%d",&x[i],&b[i]);
    42     }
    43     l=0;r=1e10;
    44     while (r-l>eps)
    45     {
    46         mid=(r+l)/2;
    47         if (run(mid)>=0) l=mid;
    48         else r=mid;
    49     }
    50     output(n);
    51     return 0;
    52 }
    View Code

    F. Special Matrices

    一个n*n的01矩阵,要求每行最多2个1,每列最多2个1,现在给出前m行,问剩下的n行有几种情况。

    按位DP,一开始想推公式,但没有推出来,比赛结束后发现可以使用DP推出结果。。。

    题目中给出前m行,也就是给出了要求的n-m行中每一列最多有几个数字1,

    不妨设有a列中不能放置数字1,b列中只能放置一个数字1,c列中能放置2个数字1。显然每列是可以交换的,因此只需从前边给出的m行中求出a,b和c的值即可。

    因为前a列不能放置数字1,因此不再考虑这些列,为了方便,我们设前b列是只能放置1个数字1,接下来c列能放置2个数字1。

    设dp[x][i][j]为到第x列,剩余i行能放置1个数字1,剩余j行能放置2个数字1。

    0<x<=b时,这些列只能放置1个数字1,状态转移有:

    dp[x][i][j]=

    dp[x-1][i+1][j]*(i+1)【从i中选1个放置一个数字1】+

    dp[x-1][i-1][j+1]*(j+1)【从j中选一个放一个数字1,此时i增加1】

    b<x<=b+c时,这些列能放置两个数字1,状态转移有:

    dp[x][i][j]=

    dp[x-1][i-2][j+2]*(j+2)*(j+1)/2+

    dp[x-1][i+2][j]*(i+2)*(i+1)/2+

    dp[x-1][i][j+1]*i*(j+1)

    为了方便,在代码中是由dp[x][i][j]直接转移到其他状态。

    显然可以使用滚动数组进行优化。这样就降成了两维

    显然我们可以知道需要填的数字1的总数,也可以求出来已经填过的数字1的个数,因此如果知道i那么j就很容易求出来(j=需要填的总数 - 已经填的数字1 - i),这样就可以降成一维的。

     1 #include<bits/stdc++.h>
     2 #define eps 0.000001
     3 #define FOR(i,j,k) for(int i=j;i<=k;i++)
     4 using namespace std;
     5 typedef long long LL;
     6 LL i,j,k,n,m,x,y,T,ans,big,cas,dp[2][505],mod,one[505],b,c,cur,sum;
     7 char s[505];
     8 bool flag;
     9 int main()
    10 {
    11     scanf("%I64d%I64d%I64d",&n,&m,&mod);
    12     for (i=1;i<=m;i++)
    13     {
    14         scanf("%s",s);
    15         for (j=0;j<n;j++)
    16         {
    17             if (s[j]=='1') one[j]++;
    18         }
    19     }
    20     
    21     for (i=0;i<n;i++)
    22     {
    23         if (one[i]==1) b++; 
    24         else 
    25         if (one[i]==0) c++;
    26     }
    27     
    28     cur=0;
    29     sum=2*(n-m);
    30     for (i=0;i<=n-m;i++)
    31     {
    32         dp[0][i]=1;
    33     }
    34     
    35     for (k=1;k<=b;k++)
    36     {
    37         for (i=0;i<=n-m;i++) dp[cur^1][i]=0;
    38         
    39         for (i=0;i<=n-m;i++)
    40         {
    41             if ((sum-(k-1)-i)%2) continue;
    42             j=(sum-(k-1)-i)/2;
    43             if (i+j>n-m) continue;
    44             
    45             if (i>=1) dp[cur^1][i-1]=(dp[cur^1][i-1]+dp[cur][i]*i)%mod;
    46             dp[cur^1][i+1]=(dp[cur^1][i+1]+dp[cur][i]*j)%mod;
    47         }
    48         cur=!cur;
    49     }
    50     
    51     sum-=b;
    52     for (k=1;k<=c;k++)//列数 
    53     {
    54         for (i=0;i<=n-m;i++) dp[cur^1][i]=0;
    55         
    56         for (i=0;i<=n-m;i++)//枚举剩余只有1个的行数 
    57         {
    58             if ((sum-(k-1)*2-i)%2) continue;
    59             j=(sum-(k-1)*2-i)/2;
    60             if (i+j>n-m) continue;
    61             
    62             if (i>=2) dp[cur^1][i-2]=(dp[cur^1][i-2]+dp[cur][i]*i*(i-1)/2)%mod;
    63             dp[cur^1][i+2]=(dp[cur^1][i+2]+dp[cur][i]*j*(j-1)/2)%mod;
    64             dp[cur^1][i]=(dp[cur^1][i]+dp[cur][i]*i*j)%mod;
    65         }
    66         cur=!cur;
    67     }
    68     
    69     printf("%I64d
    ",dp[cur][0]%mod);
    70     return 0;
    71 }
    View Code

    关于这次比赛:

    B题和F题真的很不错~~B题的贪心策略幸好之前做过类似的题目,不然就真的套用二分图匹配的算法了= =|||。。还有277.5是什么鬼Σ( ° △ °|||)︴ 

  • 相关阅读:
    抽象工厂模式
    工厂方法模式
    assert断言
    非日志警告
    requests获取所有状态码
    在线工具、资料
    重定向、feed输出:控制台输出的内容存放到文件
    正则表达式python
    python提取相对路径
    logger类
  • 原文地址:https://www.cnblogs.com/zhyfzy/p/4111135.html
Copyright © 2011-2022 走看看