emmm...今天的题考的不是很好,T2莫名挂机.
T1:
题目大意:以最简分数的形式给出一个时钟上时针与分针的夹角,时针与秒针的夹角,分针与秒针的夹角,求出所有满足条件的时间.
emmm...大水题,直接暴力枚举用double存分数水过.
考场AC代码:
#include<bits/stdc++.h> using namespace std; #define ACF inline void typedef long long LL; typedef double LD; const LD eps=0.0000001; int a1,b1,a2,b2,a3,b3,T,s,h,m,sum; LD x,y,z; struct node{ int x,y,z; }q[1000000]; ACF into(){ scanf("%d/%d%d/%d%d/%d",&a1,&b1,&a2,&b2,&a3,&b3); x=1.0*a1/b1;y=1.0*a2/b2;z=1.0*a3/b3; } bool is(double a,double b){ if (fabs(a-b)<=eps) return true; else return false; } bool check(int a,int b,int c){ LD a1,a2,a3; a3=c*6.0; a2=b*6.0+c*0.1; a1=a*30.0+b*0.5+c*(0.5/60.0); if ((is(fabs(a1-a2),x)||is(360.0-fabs(a1-a2),x))&&(is(fabs(a1-a3),y)||is(360.0-fabs(a1-a3),y))&&(is(fabs(a2-a3),z)||is(360.0-fabs(a2-a3),z))) return true; else return false; } ACF work(){ sum=0; for (h=0;h<12;h++) for (m=0;m<60;m++) for (s=0;s<60;s++) if (check(h,m,s)) q[++sum].x=h,q[sum].y=m,q[sum].z=s; } ACF outo(){ printf("%d\n",sum); for (int i=1;i<=sum;i++){ if (q[i].x<10) printf("0"); printf("%d",q[i].x); putchar(':'); if (q[i].y<10) printf("0"); printf("%d",q[i].y); putchar(':'); if (q[i].z<10) printf("0"); printf("%d",q[i].z); putchar('\n'); } } int main(){ //freopen("clock.in","r",stdin); //freopen("clock.out","w",stdout); scanf("%d",&T); while (T--){ into(); work(); outo(); } return 0; }
T2:
题目大意:
有一张n个点m条边的无向图,点从1到n编号,边权均为1.
定义一条路径为另一条一条路径的路径子序列,当且仅当以下两条件同时满足:
1、起始点与终点和另一条路径一样.
2、当前序列是的另一条路径的子序列.
现在,给定一条从点1到点n的路径,你的任务就是找到该路径中点数最少的路径子序列,为了方便,你只需要输出它的长度即可.
这道题用O(n)或O(nlog(n))的算法做就可以了.
考场上我想了一个DP,思路是这样的:
由于边都是双向边,也就是说我们可以双向行走,而题目要求的是子序列,所以我们只要存的非原始路径边(x,y)只有当x和y都在原始路径中才要存,求存的时候应与原始路径的顺序相同.
然后我们就可以用f[i]表示走到原始路径上第i个点的最短路径长度.
那么考场40分代码如下:
#include<bits/stdc++.h> using namespace std; #define ACF inline void const int N=50000; const int M=200000; int n,m,k,a[N+1],f[N+1],wh[N+1],top; struct seg{ int l,r; }e[M+1]; ACF into(){ top=0; scanf("%d%d%d",&n,&m,&k); int x,y; for (int i=1;i<=k+1;i++){ scanf("%d",&x); wh[x]=i; } for (int i=1;i<=m-k;i++){ scanf("%d%d",&x,&y); if (!wh[x]||!wh[y]) continue; if (wh[x]>wh[y]) swap(x,y); e[++top].l=wh[x];e[top].r=wh[y]; } } bool cmp(seg a,seg b){ return a.r<b.r; } ACF work(){ sort(e+1,e+1+top,cmp); int j=1; for (int i=2;i<=k+1;i++){ f[i]=f[i-1]+1; for (;e[j].r==i;j++) f[i]=min(f[i],f[e[j].l]+1); } } ACF outo(){ printf("%d\n",f[k+1]); } int main(){ //freopen("seqpath.in","r",stdin); //freopen("seqpath.out","w",stdout); int T; scanf("%d",&T); while (T--){ into(); work(); outo(); } return 0; }
DP莫名错误不知所措...
突然发现自己忘了清空数组...
改完AC代码如下:
#include<bits/stdc++.h> using namespace std; #define ACF inline void const int N=50000; const int M=200000; int n,m,k,f[N+1],wh[N+1],top; struct seg{ int l,r; }e[M+1]; ACF into(){ memset(wh,0,sizeof(wh)); memset(f,0,sizeof(f)); top=n=m=k=0; memset(e,0,sizeof(e)); scanf("%d%d%d",&n,&m,&k); int x,y; for (int i=1;i<=k+1;i++){ scanf("%d",&x); wh[x]=i; } for (int i=1;i<=m-k;i++){ scanf("%d%d",&x,&y); if (x==y) continue; if (!wh[x]||!wh[y]) continue; if (wh[x]>wh[y]) swap(x,y); e[++top].l=wh[x];e[top].r=wh[y]; } } bool cmp(seg a,seg b){ return a.r<b.r; } ACF work(){ sort(e+1,e+1+top,cmp); int j=1; f[1]=0; for (int i=2;i<=k+1;i++){ f[i]=f[i-1]+1; for (;e[j].r==i;j++) f[i]=min(f[i],f[e[j].l]+1); } } ACF outo(){ printf("%d\n",f[k+1]); } int main(){ //freopen("seqpath.in","r",stdin); //freopen("seqpath.out","w",stdout); int T; scanf("%d",&T); while (T--){ into(); work(); outo(); } return 0; }
T3:
题目大意:
平面上有n个人,每个人所在的位置可以用一个坐标(x,y)表示,其中x,y均为整数,一个人每次可以选择上下左右任意一个方向走一步,其代价为1,即(x,y)可以变为(x+1,y)、(x-1,y)、(x,y-1)、(x,y+1).
同时,定义一个位置集S是连通的当且仅当S中的任意两个位置都可以只经过S中的位置互相到达.
给定n个人所在的坐标,求使得n个人所在位置两两不同,且所在的位置集合连通的最小总代价.
一看不会,果断放弃.
推了一通n<=3的情况最终放弃.
正解是各种用贪心去中位数然后暴力枚举在这个中位数附近.
具体各类操作看每个函数的含义.
AC代码如下:
#include<bits/stdc++.h> using namespace std; #define ACF inline void typedef long long LL; const int x_[4]={0,1,0,-1},y_[4]={1,0,-1,0}; const int N=6; const LL INF=10000000000000000; int n,mx,my,px[N+5],py[N+5],fx[N+5],fy[N+5],ex[N*4+5],ey[N*4+5]; bool v[N*3+5][N*3+5]; //mx,my分别表示横纵坐标的中位数,fx,fy数组表示每个人所在的原始横纵坐标 //px,py分别表示每个人要到达的终点位置的坐标,ex,ey分别表示终点枚举的几个可供选择的点 void mid(){ //取横坐标的中位数与纵坐标的中位数 int x[N+5],y[N+5],u=(n+1)/2,d=n/2+1; for (int i=1;i<=n;i++) x[i]=fx[i],y[i]=fy[i]; sort(x+1,x+1+n);sort(y+1,y+1+n); mx=(x[u]+x[d])/2;my=(y[u]+y[d])/2; } LL calc(){ //暴力枚举排列判断哪个位置匹配哪个人 int per[N+5]={0}; LL ans=INF; for (int i=1;i<=n;i++) per[i]=i; do{ //用do-while是因为while(next_permutation)会跳过初始序列 LL s=0LL; for (int i=1;i<=n;i++) s+=abs(fx[per[i]]-px[i])+abs(fy[per[i]]-py[i]); //计算当前情况下的行走步数 ans=min(ans,s); }while (next_permutation(per+1,per+1+n)); //枚举全排列 return ans; } LL Try(int t,int s,int e){ if (t>n) return calc(); LL ans=INF; for (int i=s;i<=e;i++){ px[t]=ex[i];py[t]=ey[i]; //暴力枚举确定终点位置 int ne=e; for (int d=0;d<4;d++) if (!v[px[t]+x_[d]+N][py[t]+y_[d]+N]){ //若这个点未被占领 ex[++ne]=px[t]+x_[d],ey[ne]=py[t]+y_[d]; v[ex[ne]+N][ey[ne]+N]=1; } //暴力打标记 ans=min(ans,Try(t+1,i+1,ne)); //往下递归 for (int i=e+1;i<=ne;i++) v[ex[i]+N][ey[i]+N]=0; //回溯 } return ans; } ACF into(){ mx=my=0; memset(px,0,sizeof(px)); memset(py,0,sizeof(py)); memset(fx,0,sizeof(fx)); memset(fy,0,sizeof(fy)); //初始化清空数组 scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d%d",&fx[i],&fy[i]); mid(); } ACF work(){ for (int i=1;i<=n;i++) fx[i]-=mx,fy[i]-=my; //计算到中位数的需要的偏移量 v[N][N]=1; for (int i=1;i<=4;i++){ ex[i]=x_[i-1],ey[i]=y_[i-1]; //枚举4个点每个终点位置 v[ex[i]+N][ey[i]+N]=1; } } ACF outo(){ printf("%lld\n",Try(2,1,4)); //dfs暴力求解 } int main(){ //freopen("meet.in","r",stdin); //freopen("meet.out","w",stdout); int T; scanf("%d",&T); while (T--){ into(); work(); outo(); } return 0; }
又是一道码农题...