第十五周程序设计作业解题报告
1000.
解:直接运用sort函数即可。其实老师表示应该用重载函数把小于号的定义改掉(sort函数排完后默认数列内所有数从左到右都满足小于号),用cmp的是不是我教坏你们了 = =
PS:字符数组是有比较函数strcmp的(调用cstring库),只有我这种没上课的傻x才自己写比较函数去了。
code:
解:直接运用sort函数即可。其实老师表示应该用重载函数把小于号的定义改掉(sort函数排完后默认数列内所有数从左到右都满足小于号),用cmp的是不是我教坏你们了 = =
PS:字符数组是有比较函数strcmp的(调用cstring库),只有我这种没上课的傻x才自己写比较函数去了。
code:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <string>
using namespace std;
#define MAXN 1111111
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)>(b)?(a):(b))
#define SQR(x) ((x)*(x))
#define EPS (1e-8)
#define WMZ_MAX (2147483647)
struct data{ char name[50]; int a, b, c, d;};
int n, m;
data a[MAXN];
bool cmpp(data a, data b){
for (int i=0; 1; i++){
if (a.name[i]<b.name[i]) return true;
else if (a.name[i]>b.name[i]) return false;
if (a.name[i]==0 || b.name[i]==0 ) break;
}
return false;
}
bool cmp(data a, data b){
return (a.a>b.a)||(a.a==b.a && a.b > b.b)||(a.a==b.a && a.b==b.b && a.c > b.c)
|| (a.a==b.a && a.b==b.b && a.c == b.c && a.d > b.d) ||
(a.a==b.a && a.b==b.b && a.c == b.c && a.d == b.d && cmpp(a,b));
}
int main(){
scanf("%d", &n);
for (int i=1; i<=n; i++) {
scanf("%s%d%d%d", &a[i].name, &a[i].b, &a[i].c, &a[i].d);
a[i].a=a[i].b+a[i].c+a[i].d;
}
sort(a+1, a+1+n, cmp);
for (int i=1; i<=n; i++) printf("%s %d %d %d %d\n", a[i].name, a[i].b, a[i].c, a[i].d, a[i].a);
return 0;
}
1001.
题目大意:给定一些字母,输出他们的全排列。
解:用递归去枚举全排列,最后输出方案即可。
PS:memset函数用于将数组清零,只能用于清零赋值成其他的话会有奇怪的结果。
code:
1002.
解:单纯的实现题目,关于归并排序怎么写请见题目的hint部分以及baidu.com或者google.com
code:
1003.
解:求逆序对可以分成子问题来解决。我给出一个区间[x,y],问区间上有多少个逆序对,我们可以这样考虑。
设mid=(x+y)/2,solve(x,y)返回区间[x,y]有多少个逆序对
那么 solve[x,y] = solve(x, mid) + solve(mid+1, y) + now
显然,前两个表达式是子问题,那么now又是什么呢?
那么now就是所有ai>aj 中符合i在[x,mid]而j在[mid+1, y] 的方案数。
但是如果是暴力枚举i,j,判断是否ai>aj再进行累加的话复杂度单次已经是n^2的,显然会超时。
怎么办?
假设我们可以让[x,mid], [mid+1, y]变成两个升序的数组。那么在设两个指针i, j,且x<=i<=mid, mid+1<=j<=y,且有a[i]>a[j]
那么对于ai来讲,是不是可以得知[mid+1,y]上有(j-mid)个数比他小?那么答案+=(j-mid)即可。
而如果i是单调递减去枚举的话,那么可以得到j也一定是单调递减的(利用的数组的有序性)
所以单次的时间复杂度为n
而递归次数可知是log2(n)的
那么总的时间复杂度便是 nlogn,显然可以完成任务。
而且在理解这个做法后,你会发现其实这个做法只要在第三题归并排序的基础上加几句话就行了。
PS:还有一种nlogn求逆序对的办法在你们学习到可以支持logn复杂度求前缀和(途中可以修改且修改复杂度也是logn)的数据结构后可以试试思考看。介绍其中一种数据结构加:“树状数组”
code:
1001.
题目大意:给定一些字母,输出他们的全排列。
解:用递归去枚举全排列,最后输出方案即可。
PS:memset函数用于将数组清零,只能用于清零赋值成其他的话会有奇怪的结果。
code:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <string>
using namespace std;
#define MAXN 1111111
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)>(b)?(a):(b))
#define SQR(x) ((x)*(x))
#define EPS (1e-8)
#define WMZ_MAX (2147483647)
char a[10];
int n;
int b[10];
bool vis[10];
void dfs(int kok){
if (kok>n) {
for (int i=1; i<=n; i++) printf("%c", a[b[i]-1]);
printf("\n");
return ;
}
for (int i=1; i<=n; i++)if (vis[i]==false){
vis[i]=true;
b[kok]=i;
dfs(kok+1);
vis[i]=false;
}
}
int main(){
scanf("%s", a);
n=strlen(a);
memset(vis, 0, sizeof(vis));
dfs(1);
return 0;
}
1002.
解:单纯的实现题目,关于归并排序怎么写请见题目的hint部分以及baidu.com或者google.com
code:
#include <cstdio>
using namespace std;
#define MAXN 1111111
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)>(b)?(a):(b))
#define SQR(x) ((x)*(x))
#define EPS (1e-8)
#define WMZ_MAX (2147483647)
int a[MAXN], b[MAXN];
int n;
void gb(int x, int y){
if (x==y) b[x]=a[x];
else {
int mid=(x+y)/2;
gb(x, mid); gb(mid+1, y);
int i, j, t;
i=x; j=mid+1;
t=x;
while (i<=mid && j<=y){
if (b[i]<b[j]){
a[t++]=b[i++];
}
else {
a[t++]=b[j++];
}
}
while (i<=mid) a[t++]=b[i++];
while (j<=y) a[t++]=b[j++];
for (i=x; i<=y; i++) b[i]=a[i];
}
}
int main(){
scanf("%d", &n);
for (int i=1; i<=n; i++)scanf("%d", &a[i]);
gb(1, n);
for (int i=1; i<=n; i++) printf("%d\n", a[i]);
return 0;
}
1003.
解:求逆序对可以分成子问题来解决。我给出一个区间[x,y],问区间上有多少个逆序对,我们可以这样考虑。
设mid=(x+y)/2,solve(x,y)返回区间[x,y]有多少个逆序对
那么 solve[x,y] = solve(x, mid) + solve(mid+1, y) + now
显然,前两个表达式是子问题,那么now又是什么呢?
那么now就是所有ai>aj 中符合i在[x,mid]而j在[mid+1, y] 的方案数。
但是如果是暴力枚举i,j,判断是否ai>aj再进行累加的话复杂度单次已经是n^2的,显然会超时。
怎么办?
假设我们可以让[x,mid], [mid+1, y]变成两个升序的数组。那么在设两个指针i, j,且x<=i<=mid, mid+1<=j<=y,且有a[i]>a[j]
那么对于ai来讲,是不是可以得知[mid+1,y]上有(j-mid)个数比他小?那么答案+=(j-mid)即可。
而如果i是单调递减去枚举的话,那么可以得到j也一定是单调递减的(利用的数组的有序性)
所以单次的时间复杂度为n
而递归次数可知是log2(n)的
那么总的时间复杂度便是 nlogn,显然可以完成任务。
而且在理解这个做法后,你会发现其实这个做法只要在第三题归并排序的基础上加几句话就行了。
PS:还有一种nlogn求逆序对的办法在你们学习到可以支持logn复杂度求前缀和(途中可以修改且修改复杂度也是logn)的数据结构后可以试试思考看。介绍其中一种数据结构加:“树状数组”
code:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <string>
using namespace std;
#define MAXN 1111111
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)>(b)?(a):(b))
#define SQR(x) ((x)*(x))
#define EPS (1e-8)
#define WMZ_MAX (2147483647)
int a[MAXN], b[MAXN];
int n;
long long ans;
void gb(int x, int y){
if (x==y) {
b[x]=a[x];
return ;
}
else {
int mid=(x+y)/2;
gb(x, mid); gb(mid+1, y);
int i, j, t;
i=mid; j=y;
for (int i=mid; i>=x; i--){
while (j>mid && b[j]>=a[i]) j--;
ans+=(j-mid);
}
i=x; j=mid+1;
t=x;
while (i<=mid && j<=y){
if (b[i]<b[j]){
a[t++]=b[i++];
}
else {
a[t++]=b[j++];
}
}
while (i<=mid) a[t++]=b[i++];
while (j<=y) a[t++]=b[j++];
for (i=x; i<=y; i++) b[i]=a[i];
}
}
int main(){
scanf("%d", &n);
for (int i=1; i<=n; i++)scanf("%d", &a[i]);
ans=0;
gb(1, n);
//for (int i=1; i<=n; i++) printf("%d\n", a[i]);
cout << ans << endl;
return 0;
}