记录week4的实验三道题+作业三道题
DDL的恐惧、四个数列、 TT 的神秘礼物、咕咕东的奇遇、咕咕东想吃饭、可怕的宇宙射线
作业:
DDL的恐惧
ZJM 有 n 个作业,每个作业都有自己的 DDL,如果 ZJM 没有在 DDL 前做完这个作业,那么老师会扣掉这个作业的全部平时分。
所以 ZJM 想知道如何安排做作业的顺序,才能尽可能少扣一点分。
请你帮帮他吧!
Input
输入包含T个测试用例。输入的第一行是单个整数T,为测试用例的数量,每个测试用例以一个正整数N开头(1<=N<=1000),表示作业的数量。然后两行。第一行包含N个整数,表示DDL,下一行包含N个整数,表示扣的分。Output
对于每个测试用例,您应该输出最小的总降低分数,每个测试用例一行。Sample Input
3
3
3 3 3
10 5 1
3
1 3 1
6 2 3
7
1 4 6 4 2 4 3
3 2 1 7 6 5 4
Sample Output
0
3
5
本题应应用贪婪算法,将ddl按照扣分值从大到小排序,优先安排大的,建立时间数组用来表示时间点是否安排了任务,然后遍历每个ddl,将其安排进时间数组,最终得到的就是最少扣分。
#include<iostream>
#include<algorithm>
using namespace std;
int T,N;
int *time;
struct WORK{
int ddl;
int kf;
};
WORK*work;
bool cmp(WORK a,WORK b){
return a.kf>b.kf;
}
int sum=0;
void greedy(){
cin>>T;
for(int t=0;t<T;t++){
int re=0;sum=0;
cin>>N;work=new WORK[N];int maxtime=0;
for(int i=0;i<N;i++){cin>>work[i].ddl;if(work[i].ddl>maxtime)maxtime=work[i].ddl;}
for(int i=0;i<N;i++){cin>>work[i].kf;sum=sum+work[i].kf;}
time=new int[maxtime+1];for(int i=0;i<maxtime+1;i++)time[i]=0;time[0]=1;
sort(work,work+N,cmp);
for(int i=0;i<N;i++){
WORK p=work[i];int j=p.ddl;
while(j>0){
if(time[j]==0)
{
time[j]=1;re=re+p.kf;break;
}
j--;
}
}
cout<<sum-re<<endl;
}
}
int main()
{
greedy();
return 0;
}
四个数列
这里ZJM 有四个数列 A,B,C,D,每个数列都有 n 个数字。ZJM 从每个数列中各取出一个数,他想知道有多少种方案使得 4 个数的和为 0。
当一个数列中有多个相同的数字的时候,把它们当做不同的数对待。
请你帮帮他吧!
Input 第一行:n(代表数列中数字的个数) (1≤n≤4000)
接下来的 n 行中,第 i 行有四个数字,分别表示数列 A,B,C,D 中的第 i 个数字(数字不超过 2 的 28 次方)
Output 输出不同组合的个数。
Sample Input
6
-45 22 42 -16
-41 -27 56 30
-36 53 -37 77
-36 30 -75 -46
26 -38 -10 62
-32 -54 -6 45Sample Output
5
本题暴力枚举的话复杂度n^4,无法通过oj,故采用二分的方法,计算AB之和、CD之和,然后枚举AB之和,在CD之和数组中找到等于该数相反数的树的数目,这其中用二分查找的方式查找其第一个大于等于该数的和最后一个大于等于该数的,故复杂度为n*n
#include<iostream>
#include<algorithm>
using namespace std;
int n;
int a[4005];int b[4005];int c[4005];int d[4005];
int *A,*B;
int finde(int k){
int l,r,mid;int ans=-1;
l=0;r=n*n-1;
while(l<=r){
mid=(l+r)/2;
if(B[mid]==k){ans=mid;l=mid+1; }
else if(B[mid]<k)l=mid+1;
else if(B[mid]>k)r=mid-1;
}
return ans;
}
int finds(int k){
int l,r,mid;int ans=-1;
l=0;r=n*n-1;
while(l<=r){
mid=(l+r)/2;
if(B[mid]==k){ans=mid;r=mid-1; }
else if(B[mid]<k)l=mid+1;
else if(B[mid]>k)r=mid-1;
}
return ans;
}
bool cmp(int a,int b){
return a<b;
}
void meun(){
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i]>>b[i]>>c[i]>>d[i];
}
A=new int[n*n];
B=new int[n*n];int cc=0;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
A[cc]=a[i]+b[j];
B[cc]=c[i]+d[j];
cc++;
}
// sort(A,A+n*n,cmp);
sort(B,B+n*n,cmp);int sum=0;
// for(int i=0;i<n*n;i++)cout<<B[i]<<" ";cout<<endl;
for(int i=0;i<n*n;i++)
{
int k=-1*A[i];
int s=finds(k);
int e=finde(k);
if(s!=-1&&e!=-1){
sum=sum+e-s+1; }
}
cout<<sum<<endl;
}
int main()
{
meun();
return 0;
}
TT 的神秘礼物
给定一个 N 个数的数组 cat[i],并用这个数组生成一个新数组 ans[i]。新数组定义为对于任意的 i, j 且 i != j,均有 ans[] = abs(cat[i] - cat[j]),1 <= i < j <= N。试求出这个新数组的中位数,中位数即为排序之后(len+1)/2 位置对应的数字,’/’ 为下取整。
Input 多组输入,每次输入一个 N,表示有 N 个数,之后输入一个长度为 N 的序列 cat, cat[i] <= 1e9 , 3 <=n <= 1e5Output 输出新数组 ans 的中位数
Sample Input
4
1 3 2 4
3
1 10 2
Sample Output
1
8
这道题用二分答案来做,直接暴力枚举的话组合数量级为1e10 ,显然无法通过,故采用二分答案。
观察题目,找到中位数也就是找到一个数p使得 xi-xj<=p 的数目为 n*(n-1)/4(先将数组去绝对值)。故可以将问题转化为: 对答案区间进行折半,选择一个答案并对每次选择的答案mid判断有多少(i ,j)数对的差值比mid大;其中判断有多少数对可以继续用二分,即:对每一个i,二分查找比xj+mid大的数目。
#include <iostream>
#include<algorithm>
using namespace std;
int n;int x[100005];
int zws;
int finds(int k){
int l,r,mid;int ans=n;
l=0;r=n-1;
while(l<=r){
mid=(l+r)/2;
if(x[mid]>=k){ans=mid;r=mid-1; }
else if (x[mid]<k){
l=mid+1;}
}
return ans;
}
bool zb(int k){
long long int cnt = 0;
for(int i = 0; i < n; i++)
{
int t=finds(x[i]+k);//会TLE????????
cnt += n-t;//lower_bound(x+i+1, x+n, x[i]+k);
}
//cnt计算所有差值比mid大的原数列组合个数
return cnt <= zws/2;
}
int main(){
while(scanf("%d",&n)!=EOF){
for(int i=0;i<n;i++)
scanf ("%d",&x[i]);
sort(x,x+n);
zws= n*(n-1)/2; //元素个数
int left = 0,right=x[n-1]; //最小元素 最大元素
while(right-left > 1) {
int mid = (left+right)/2;
if(zb(mid)) right = mid;
else left = mid;
}
cout<<left<<endl;
}
return 0;
}
实验:
咕咕东的奇遇
大致意思就是给定一个字母按顺序构成的环,z—>ab********yz —>a其中a是0,逆时针依次递增,初始指向a,没改变一次指向都算一步,且每次只能改变到相邻的一个字母。然后给定一个字符串,求指完这个字符串需要至少多少步。
做法大致就是使其默认顺时针转,判断当前到下一个字符是否大于13,若大于十三则逆时针转。
#include<iostream>
#include<cmath>
using namespace std;
int main(){
char c;
int now=0;
int next;
int R;
char a[26];
for(int i=0;i<26;i++)a[i]='a'+i;
// cout<<a[5]<<endl;
int sum=0;
string s;
cin>>s;
for(int i=0;i<s.length();i++)
{
c=s[i];
cout<<c<<endl;
next=c-a[now];
if(abs(next)>13)
{
sum=sum+26-abs(next);//cout<<abs(next)<<endl;
now=c-'a';
}
else if(abs(next)<=13)
{
sum=sum+abs(next);
now=c-'a';
//cout<<abs(next)<<endl;
}
}
cout<<sum<<endl;
return 0;
}
咕咕东想吃饭
这道题大致意思是:在以后的n天里,每天吃若干生煎,其购买方式有两种,一种是一次买两个,另一种是今天一个明天一个,然后每天购买次数不限,但是不能有剩下的也不能不够,问能不能达成以后n天天天满足输入的数量。
做法大致是:因为今天购买的最多只影响明天,且影响无法通过为值零的天。故将其以零分段存储,然后判断每段是否为偶数,若存在为奇数的则不能达成。
#include<iostream>
using namespace std;
int main (){
int n;
cin>>n;
int *a=new int [n];
long long int *b=new long long int [n];
int _count=0; long long int temp=0;
for(int i=0;i<n;i++){
cin>>a[i];
temp=temp+a[i];
if(a[i]==0||i==n-1){
b[_count]=temp;
_count++;
temp=0;
}
}
int p=0;
for(int i=0;i<_count;i++)
p=p+b[i]%2;
if(p%2==0)
cout<<"YES";
else cout<<"NO";
return 0;
}
可怕的宇宙射线
这样的一个线段,从红色开始,默认向上,每隔一段距离就分裂一次,计算占据的格子数目。
这道题可以使用bfs或者dfs。我使用了bfs。因为数据量比较大,最后会分裂30次导致需要进行剪枝,判断需不需要加入队列。
具体做法是:初始化地图大小为320*320、 用结构体数组offset[8]表示位移,用node节点表示每次分裂后的第一个点;struct node{ int x; int y; int px; int py; };
其中x,y为当前位置,px,py为上一步位置,利用x y和px py确定方向,每次进行分裂时,先判断该情况是否已经加入过了,记忆数组bool rem[320][320][40][40];
其第一第二维表示坐标,第三维表示方向,第四维表示当前轮数(这里要注意,必须以当前轮数而不是当前节点将要走的步数作为第四维判断,否则可能出现 尽管当前节点下一轮将走的步数等于此前push进队列的某一节点的下一轮将要走的步数但是由于不是在同一轮分裂,当前判断的节点以后的某一步可能和此前push进队列的某一节点以后的某一步不同,从而产生不同的路径,若将该当前节点忽略,就会产生错误。
//#include<bits/stdc++.h>
#include<iostream>
#include<queue>
using namespace std;
struct offset{
int dx;
int dy;
offset(){
dx=0;
dy=0;
}
}offset[8];
struct node{
int x;
int y;
int px;
int py;
};
bool rem[320][320][40][40];
int map[320][320];
int n;int *step;
void pre(){
for(int i=0;i<320;i++)
for(int j=0;j<320;j++)
map[i][j]=0;
offset[0].dx=0;offset[0].dy=1;//上
offset[1].dx=1;offset[1].dy=1;//右上
offset[2].dx=1;offset[2].dy=0;//右
offset[3].dx=1;offset[3].dy=-1;//右下
offset[4].dx=0;offset[4].dy=-1;//下
offset[5].dx=-1;offset[5].dy=-1;//左下
offset[6].dx=-1;offset[6].dy=0;//左
offset[7].dx=-1;offset[7].dy=1;//左上
}
int judge(int dx,int dy){
if(dx==0&&dy==1)return 0;
if(dx==1&&dy==1)return 1;
if(dx==1&&dy==0)return 2;
if(dx==1&&dy==-1)return 3;
if(dx==0&&dy==-1)return 4;
if(dx==-1&&dy==-1)return 5;
if(dx==-1&&dy==0)return 6;
if(dx==-1&&dy==1)return 7;
}
int sum(){
int _count=0;
for(int i=0;i<320;i++)
{
for(int j=0;j<320;j++)
{
if(map[i][j]==1)_count++;
}
}
return _count;
}
queue<node>q;
int main() {
cin>>n;
step=new int [n];
for(int i=0;i<n;i++)cin>>step[i];
pre();
node newl,newr; int left,right;
node start;start.x=160;start.y=160;start.px=160;start.py=159;
q.push(start);
rem[start.x][start.y][0][0]=1;
for(int i=0;i<n;i++)
{
int ssr=q.size();
int s=step[i];
while(ssr>0){
node now=q.front();
q.pop();ssr--;
map[now.x][now.y]=1;
int dx=now.x-now.px; int dy=now.y-now.py;
int dir=judge(dx,dy);
int nx=now.x;int ny=now.y;
for(int j=0;j<s-1;j++){
nx=nx+offset[dir].dx;
ny=ny+offset[dir].dy;
map[nx][ny]=1;
}
left=(dir+7)%8;
right=(dir+1)%8;
newl.x=nx+offset[left].dx;
newl.y=ny+offset[left].dy;
newl.px=nx;newl.py=ny;
newr.x=nx+offset[right].dx;
newr.y=ny+offset[right].dy;
newr.px=nx;newr.py=ny;
if(i<n-1){
if(rem[newl.x][newl.y][left][i]!=true){
q.push(newl);rem[newl.x][newl.y][left][i]=true;
}
if(rem[newr.x][newr.y][right][i]!=true){
q.push(newr);rem[newr.x][newr.y][right][i]=true;
}
}
}
}
int re=sum();
cout<<re;
return 0;
}