比赛页面: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 }
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 }
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 }
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 }
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 }
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 }
关于这次比赛:
B题和F题真的很不错~~B题的贪心策略幸好之前做过类似的题目,不然就真的套用二分图匹配的算法了= =|||。。还有277.5是什么鬼Σ( ° △ °|||)︴