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\n",m);
43     for (int i=0;i<m;i++)
44     {
45         printf("%d %d\n",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\n",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\n");
28             return 0;
29         }else
30         {
31             printf("-1 -1\n");
32             return 0;
33         }
34     }
35     if (s>m*9)
36     {
37         printf("-1 -1\n");
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("\n");
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\n",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\n",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\n",dp[cur][0]%mod);
70     return 0;
71 }
View Code

关于这次比赛:

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

转载于:https://www.cnblogs.com/zhyfzy/p/4111135.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值