完成度 (11 / 12)
Problem A
题目大意
给定一个序列,在n次交换次数内,使得n个数字升序排列。给出一种方案。
解题分析
求出每个数字对应升序排列的位置,依次交换即可。
参考程序
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 5 const int maxn=3008; 6 7 struct node{ 8 int x,y; 9 }b[maxn],f[maxn]; 10 11 int a[maxn],rk[maxn]; 12 int n; 13 14 int cmp(node a,node b){ 15 return a.x < b.x; 16 } 17 18 int main(){ 19 scanf("%d",&n); 20 for (int i = 1;i <= n;i++) { 21 scanf("%d",&a[i]); 22 b[i].x = a[i]; 23 b[i].y = i; 24 } 25 sort(b+1,b+n+1,cmp); 26 for (int i = 1;i <= n;i++) { 27 rk[b[i].y] = i; 28 } 29 30 31 int ans = 0; 32 int i = 1; 33 while (i <= n) { 34 if (i != rk[i]) { 35 int j = rk[i]; 36 ans++; 37 f[ans].x = i; 38 f[ans].y = j; 39 swap(a[i],a[j]); 40 swap(rk[i],rk[j]); 41 } else i++; 42 } 43 printf("%d ",ans); 44 for (int i=1;i<=ans;i++) printf("%d %d ",f[i].x-1,f[i].y-1); 45 }
Problem B
题目大意
给定一张有向图,求由4个点组成的菱形数量。
解题分析
按照题意暴力求解。。
参考程序
1 #include <cstdio> 2 #include <cstring> 3 using namespace std; 4 5 #define maxn 3008 6 #define maxm 60008 7 8 int n,m,lt[maxn],f[maxn],sum=1; 9 10 struct line 11 { 12 int u,v,nt; 13 }eg[maxm]; 14 15 void add(int u,int v) 16 { 17 eg[++sum].u=u; eg[sum].v=v; eg[sum].nt=lt[u]; lt[u]=sum; 18 } 19 20 int main(){ 21 int n,m; 22 scanf("%d%d",&n,&m); 23 for (int i=1;i<=m;i++) { 24 int u,v; 25 scanf("%d%d",&u,&v); 26 add(u,v); 27 } 28 int ans=0; 29 for (int u=1;u<=n;u++) { 30 memset(f,0,sizeof(f)); 31 for (int i=lt[u];i;i=eg[i].nt){ 32 int v=eg[i].v; 33 for (int j=lt[v];j;j=eg[j].nt){ 34 int w=eg[j].v; 35 if (w != u) { 36 ans+=f[w]; 37 f[w]++; 38 } 39 } 40 } 41 } 42 printf("%d ",ans); 43 44 }
Problem C
题目大意
有n个点,每个点有两个值,分别表示距离起点的距离xi,以及所获得的愉悦值bi。
有一个人向从起点走到n号点,期望每天所走路程为定值L。
定义总体失望值 sum = sigma(sqrt(Ri - L)) / sigma(bi); Ri为第i天所走路程,bi为第i天到达节点的愉悦值
最小化sum,给出此时的方案。
解题分析
二分答案,转化为判定性问题。用DP来判定。
参考程序
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <cmath> 5 using namespace std; 6 7 int n,L; 8 int x[1008],b[1008],pre[1008]; 9 double dp[1008]; 10 11 int check(double P) { 12 dp[0]=0; 13 memset(pre,-1,sizeof(pre)); 14 for (int i=1;i<=n;i++){ 15 dp[i]=1e8; 16 for (int j=0;j<i;j++){ 17 int r=x[i]-x[j]; 18 double tmp = dp[j]+sqrt(fabs(r-L)) - b[i] * P ; 19 if (tmp < dp[i]) { 20 dp[i] = tmp; 21 pre[i]=j; 22 } 23 } 24 } 25 return dp[n]>0; 26 } 27 28 void dfs(int x){ 29 if (pre[x]>=1) 30 dfs(pre[x]); 31 printf("%d ",x); 32 } 33 34 int main() { 35 scanf("%d%d",&n,&L); 36 for (int i=1;i<=n;i++) { 37 scanf("%d%d",&x[i],&b[i]); 38 } 39 double l=0,r=1e8,mid; 40 while (r - l >1e-8) { 41 double mid = (l + r) / 2; 42 if (check(mid)) l=mid; else r=mid; 43 } 44 dfs(n); 45 46 }
Problem D
题目大意
定义一个n*n的矩形,矩形中只能填0或1,且每行每列的数字和不超过2。
给定矩阵的前m行填法,求填完整个矩形的方案数。
解题分析
注意到矩形的每行每列可以交换顺序,用DP来解决。
令f[i][j][k]表示前i行填了j个1,k个2。每次转移有3种方案。
注意剪枝,如果当前的f[i][j][k]为0则跳过,不然会T。
要用滚动数组,不然会MLE。
参考程序
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 using namespace std; 5 6 #define maxn 508 7 #define LL long long 8 int a[maxn]; 9 LL dp[2][maxn][maxn]; 10 int n,m,mo; 11 12 void add(int &x,int y){ 13 x = x + y; 14 if (x > mo) x -= mo; 15 } 16 int main(){ 17 scanf("%d%d%d",&n,&m,&mo); 18 for (int i=1;i<=m;i++){ 19 char C[maxn]; 20 scanf("%s",C+1); 21 for (int i=1;i<=n;i++) a[i]+=C[i]-'0'; 22 } 23 int x=0,y=0; 24 for (int i=1;i<=n;i++) { 25 if (a[i]==1) y++; 26 if (a[i]==0) x++; 27 } 28 dp[0][x][y]=1; 29 int i = 0; 30 for (int ii=m+1;ii<=n;ii++) { 31 memset(dp[i^1],0,sizeof(dp[i^1])); 32 for (int j=0;j<=n;j++) 33 for (int k=0;k<=n;k++) 34 if (dp[i][j][k]) { 35 if (j>=2) 36 dp[i^1][j-2][k+2] = (dp[i^1][j-2][k+2] + dp[i][j][k] * j * (j-1) / 2) % mo; 37 if (j>=1 && k>=1) 38 dp[i^1][j-1][k] = (dp[i^1][j-1][k] + dp[i][j][k] * j * k) % mo; 39 if (k>=2) 40 dp[i^1][j][k-2] = (dp[i^1][j][k-2] + dp[i][j][k] * k * (k-1) / 2 ) % mo; 41 } 42 i^=1; 43 } 44 printf("%I64d ",dp[i][0][0]); 45 }
Problem E
题目大意
给定2个序列,若ai与bj的差值小于等于1,则i和j可以配对。求最大配对数
题目分析
排序之后,扫一遍。
参考程序
1 #include <cstdio> 2 #include <algorithm> 3 #include <cmath> 4 using namespace std; 5 6 int a[1008],b[1008]; 7 int n,m; 8 9 int main(){ 10 scanf("%d",&n); 11 for (int i=1;i<=n;i++) scanf("%d",&a[i]); 12 scanf("%d",&m); 13 for (int i=1;i<=m;i++) scanf("%d",&b[i]); 14 int ans=0; 15 sort(a+1,a+n+1); 16 sort(b+1,b+m+1); 17 18 int i=1,j=1; 19 while (i<=n && j<=m) { 20 if (abs(a[i] - b[j])<=1) { 21 ans++; 22 i++; 23 j++; 24 continue;; 25 } 26 if (a[i]>b[j]) { 27 j++; 28 continue; 29 } 30 if (a[i]<b[j]) { 31 i++; 32 continue; 33 } 34 } 35 printf("%d ",ans); 36 37 }
Problem F
题目大意
给定n和S,求最大和最小位数为n,数字和为S的数字。
题目分析
贪心即可。注意特判0。
参考程序
1 #include <cstdio> 2 #include <cstring> 3 4 int n,s; 5 int a[1000]; 6 7 void getmin(int n,int s) { 8 memset(a,0,sizeof(a)); 9 int i=n; 10 while (i>=1 && s>0) { 11 if (s>=9) { 12 a[i]=9; 13 s-=9; 14 i--; 15 } else { 16 a[i]=s; 17 s=0; 18 i--; 19 } 20 } 21 if (i==0 && s>0) printf("-1 "); else 22 { 23 if (a[1]==0) { 24 a[1]=1; 25 a[i+1]-=1; 26 } 27 for (int i=1;i<=n;i++) printf("%d",a[i]); 28 printf(" "); 29 } 30 } 31 32 void getmax(int n,int s) { 33 memset(a,0,sizeof(a)); 34 int i=1; 35 while (i<=n && s>0) { 36 if (s>=9) { 37 a[i]=9; 38 s-=9; 39 i++; 40 } else { 41 a[i]=s; 42 s=0; 43 i++; 44 } 45 } 46 if (i==n+1 && s>0) printf("-1 "); else 47 { 48 for (int i=1;i<=n;i++) printf("%d",a[i]); 49 printf(" "); 50 } 51 } 52 53 int main(){ 54 scanf("%d%d",&n,&s); 55 if (n==1&&s==0) printf("0 0 "); else 56 if (s==0) printf("-1 -1 ") ; 57 else { 58 getmin(n,s); 59 getmax(n,s); 60 } 61 62 }
Problem G
题目大意
定义一个集合的价值为该集合的最小元素。
给定一个集合,求有奇数个元素组成的子集和偶数个元素组成子集价值总和差值的绝对值。
题目分析
写的是枚举最小值组合数求解。
静下心来想想,貌似就是最大值,给消的都消掉了。
参考程序
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 5 long long ans; 6 int n,T; 7 int a[100],C[100][100]; 8 9 void init(){ 10 ans = 0; 11 } 12 13 int main(){ 14 15 for (int i=0;i<=50;i++) C[i][0] = 1; 16 C[1][1] = 1; 17 for (int i=2;i<=50;i++) 18 for (int j=1;j<=i;j++) 19 C[i][j] = C[i-1][j] + C[i-1][j-1]; 20 scanf("%d",&T); 21 while (T--) { 22 init(); 23 scanf("%d",&n); 24 for (int i = 1;i <= n;i++) scanf("%d",&a[i]); 25 sort(a+1,a+n+1); 26 27 int p; 28 for (int i=1;i<=n;i++) { 29 if (i & 1) p = 1 ; else p = -1; 30 for (int j = 1;j<=n - i + 1;j++) { 31 ans = ans + 1ll*a[j] * C[n - j][i - 1] * p; 32 } 33 } 34 if (ans < 0) ans = -ans; 35 printf("%I64d ",ans); 36 } 37 }
Problem H
题目大意
题目分析
参考程序
Problem I
题目大意
定义幸运数字为有相等数量的4和7组成的数字。
求超过给定数字的最小的幸运数字。
题目分析
直接打表求出所有的幸运数字。
模拟的话有点麻烦的样子。
参考程序
1 #include <cstdio> 2 3 int a[20]; 4 int len; 5 int num; 6 long long b[100000]; 7 void work(){ 8 long long ans=0; 9 for (int i=1;i<=len;i++) 10 ans=ans*10+a[i]; 11 b[++num]=ans; 12 } 13 void dfs(int x,int s4,int s7){ 14 if (x==len+1) { 15 work(); 16 return; 17 } 18 if (s4>0) { 19 a[x]=4; 20 dfs(x+1,s4-1,s7); 21 } 22 if (s7>0) { 23 a[x]=7; 24 dfs(x+1,s4,s7-1); 25 } 26 } 27 28 int T; 29 int main(){ 30 num = 0; 31 for (len=2;len<=18;len+=2) 32 dfs(1,len / 2, len / 2); 33 scanf("%d",&T); 34 while (T--) { 35 long long x; 36 int ok=0; 37 scanf("%I64d",&x); 38 for (int i=1;i<=num;i++) 39 if (b[i]>=x) { 40 printf("%I64d ",b[i]); 41 ok=1; 42 break; 43 } 44 if (!ok) printf("44444444447777777777 "); 45 } 46 }
Problem J
题目大意
给定n米长的杆子,将杆子拆分成尽可能多的部分,使得任意三部分不能组成三角形。
题目分析
按照斐波那契数列分就行了。
参考程序
1 #include <cstdio> 2 3 int T; 4 long long n,a,b,c,sum; 5 6 void write(int x){ 7 printf("%d ",x); 8 } 9 int main(){ 10 scanf("%d",&T); 11 while (T--) { 12 scanf("%I64d",&n); 13 int ans=0; 14 if (n==1) write(1); else 15 if (n==2) write(1); else 16 if (n==3) write(2); else 17 if (n==4) write(2); else 18 if (n==5) write(2); else 19 if (n==6) write(3); else 20 { 21 ans = 3; 22 a = 1; 23 b = 2; 24 c = 3; 25 sum = 6; 26 while (sum <= n) { 27 a = b; 28 b = c; 29 c = a + b; 30 sum = sum + c; 31 ans ++ ; 32 } 33 write(ans-1); 34 } 35 36 } 37 38 }
Problem K
题目大意
两个人进行博弈,有n个数字,每人每次可以拿任意多的数字,所得价值为所得数字中的最小值。
询问先手与后手所得价值总和的差的最大值。
题目分析
首先对n个数字从小到大排序。
可以发现每次最大的数字必定是要拿的。
令f[i]为前i个数字进行游戏的答案。
f[i] = a[i] - min( f[j] ) ( j <= i )
参考程序
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 5 int T,n,m,now; 6 int a[50008]; 7 8 int main(){ 9 scanf("%d",&T); 10 while (T--) { 11 scanf("%d",&n); 12 for (int i=1;i<=n;i++) scanf("%d",&a[i]); 13 sort(a+1,a+n+1); 14 15 int ans=0; 16 for (int i=1;i<=n;i++) 17 if (a[i]-ans > ans) 18 ans = a[i] - ans; 19 printf("%d ",ans); 20 } 21 }
Problem L
题目大意
有m个服务员,和n个顾客,给出每个服务员招待每个顾客的时间,每个服务员在同一时间只能服务一个顾客,询问所有顾客完成服务的最少时间。
解题分析
第一反应就是用费用流来做。
题目难度在于每个服务员在同一时间只能服务一个顾客。考虑把每个服务员拆成n个点,表示服务员的服务顺序。
若第i个顾客是第j个服务员的倒数第k个服务对象,那么对答案的贡献为V[i,j]*k (使k-1个顾客等待了V[i,j]的时间)
因此由S向每个顾客连一条(1,0)的边,第i个顾客向第j个服务员的第k个点连一条(1,v[i,j]*k)的边,每个服务员的n个点向T’连一条(1,0)的边,T'向T连一条(n,0)的边。
参考程序
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 using namespace std; 6 7 #define V 1008 8 #define E 1000008 9 #define INF 200000000 10 int n,m,S,T,Que; 11 12 int pd[V],dis[V],pre[V]; 13 struct line{ 14 int u,v,c,w,nt; 15 }eg[E]; 16 int lt[V],sum; 17 18 void adt(int u,int v,int c,int w) { 19 eg[++sum].u=u; eg[sum].v=v; eg[sum].c=c; 20 eg[sum].w=w; eg[sum].nt=lt[u]; lt[u]=sum; 21 } 22 23 void add(int u,int v,int c,int w) { 24 adt(u,v,c,w); adt(v,u,0,-w); 25 //printf("%d %d %d %d ",u,v,c,w); 26 } 27 28 void init(){ 29 30 sum=1; 31 memset(lt,0,sizeof(lt)); 32 scanf("%d%d",&m,&n); 33 S = 0; T = (m+1)*n+2; 34 for (int i=1;i<=n;i++) add(S,i,1,0); 35 for (int i=1;i<=n;i++) 36 for (int j=1;j<=m;j++) { 37 int x; 38 scanf("%d",&x); 39 for (int k=1;k<=n;k++) 40 add(i,j*n+k,1,k*x); 41 } 42 for (int i=n+1;i<=(m+1)*n;i++) 43 add(i,T-1,1,0); 44 add(T-1,T,n,0); 45 n=T; 46 } 47 48 bool spfa() { 49 queue <int> Q; 50 for (int i=0;i<=n;i++) { 51 dis[i]=INF; 52 pd[i]=0; 53 pre[i]=-1; 54 } 55 dis[S]=0; pd[S]=1; Q.push(S); 56 while (!Q.empty()) { 57 int u = Q.front(); 58 for (int i=lt[u];i;i=eg[i].nt) 59 if (eg[i].c>0) { 60 int v=eg[i].v; 61 if (dis[u]+eg[i].w<dis[v]) { 62 dis[v]=dis[u]+eg[i].w; 63 pre[v]=i; 64 if (!pd[v]) { 65 Q.push(v); 66 pd[v]=1; 67 } 68 } 69 } 70 pd[u]=0; 71 Q.pop(); 72 } 73 return dis[T]!=INF; 74 } 75 76 void minCmaxF(){ 77 int minC=0,maxF=0,flow; 78 while (spfa()) { 79 flow=INF; 80 for (int i=pre[T];~i;i=pre[eg[i].u]) 81 flow=min(flow,eg[i].c); 82 for (int i=pre[T];~i;i=pre[eg[i].u]) { 83 eg[i].c-=flow; 84 eg[i^1].c+=flow; 85 } 86 maxF+=flow; 87 minC+=flow*dis[T]; 88 } 89 printf("%d ",minC); 90 91 } 92 93 int main(){ 94 scanf("%d",&Que); 95 while (Que--) { 96 init(); 97 minCmaxF(); 98 } 99 }