HDU 2011 菜鸟杯
A:该题写了很久,一直TLE,主要是枚举到n/2时间复杂度实在太高了,其实嘛,这道题就是因式分解,所以就是i*i=n,也就是sqrt(n)
#include<stdio.h> #include<math.h> int main() { int n,T,i; int a,b; scanf("%d",&T); while(T--) { scanf("%d",&n); for(i=sqrt(1.0*n); i>=1; i--) { if(n%i==0) { a=i; b=n/i; if(a<b) {int t=a;a=b;b=t;} if((a+b)%2==0 && (a-b)%2==0 && a!=b)//拆分后,组成二元一次方诺 { break; } } } if(i==0) printf("-1\n"); else printf("%d\n",(a-b)/2); } return 0; }
B:纯水
#include<stdio.h> #include<math.h> int Is_what(char ch) { if(ch>='0' && ch<='9') return 1; return 0; } int main() { char str[10010]; int i,j,ans,T; while(scanf("%d",&T)!=EOF) { scanf("%s",str); for(i=0; str[i]; i+=5) { ans=0; for(j=i; j<5+i; j++) { int t=Is_what(str[j]); if(t==1) ans+=pow(2,4-j+i); } printf("%c",ans+65); } printf("\n"); } }
C:给两个炮台和许多个敌人的坐标,问两个炮台的射程分别是多少的时候,可以既覆盖所有敌点,又花费最少(R1^2+R2^2)
错误思路:刚开始是依次求出每个点到炮台disA和disB,比较两个大小,若A>B,则将该点赋予B炮台,这种思路是错的,比如AB距离为100,他们中点旁边各有1个敌人,根据这种思路得到的距离为49*49 + 49 * 49 = 4802,而如果只用A,则只需要51 * 51 = 2601.所以这种思路是错误的。(这种反例数据很好)。
正确思路:将所有敌人距离A的距离从大到小排序,然后枚举A的半径,则B的半径就是A无法覆盖的点中距离B最远的距离。然后用一个变量维护A和B距离的最小值即可。
#include<stdio.h> #include<algorithm> using namespace std; const int MAXN=100010; struct PNT { int x,y; int disA,disB; }p1,p2,pnt[MAXN]; int _dist(PNT a,PNT b) { return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y); } bool cmp(PNT a,PNT b) { return a.disA>b.disA; } int main() { int T,n; int i; scanf("%d",&T); while(T--) { scanf("%d%d%d%d",&p1.x,&p1.y,&p2.x,&p2.y); scanf("%d",&n); for(i=0;i<n;i++) { scanf("%d%d",&pnt[i].x,&pnt[i].y); pnt[i].disA=_dist(p1,pnt[i]); pnt[i].disB=_dist(p2,pnt[i]); } sort(pnt,pnt+n,cmp); int res=pnt[0].disA; int ans=0; /*由于A已经排序了,所以没有涉及的地方就是没有覆盖的,因为他必定比临界的那点要大 所以可以直接查找那些没涉及的B的最大值(当前的点,以前最大的记录点) */ for(i=1;i<n;i++) { ans=max(ans,pnt[i-1].disB); res=min(res,ans+pnt[i].disA); } //A一个点都没有涉及 ans=max(pnt[n-1].disB,ans); res=min(res,ans); printf("%d\n",res); } }
D:
E:水题ABC分别代表不同的值,给你几窜ABC求几窜中的一窜总和最小的
还是wa的找不出来错误,错在当把ABC的记过预处理的,数组ch必须定义成int而不是char
#include<stdio.h> #include<algorithm> int main() { char ch[100]; int n,B,D,f,F; char str[1000000]; while(scanf("%d",&n)!=EOF) { scanf("%d%d%d%d",&B,&D,&f,&F); int res=10000000; getchar(); while(n--) { gets(str); int ans=0; for(int i=0; str[i]; i++) { if(str[i]=='A') ans+=B+D+f; if(str[i]=='B') ans+=B*2+D*2+F; if(str[i]=='C') ans+=B*3+D*3+F*2; } if(res>ans) res=ans; } printf("%d\n",res); } return 0; }
F:这个坑爹。。。。规律完全看不出来,看解题报告后恍然大悟,
S(1)=1,
S(2)=11,(1个1)
S(3)=21,(2个1)
S(4)=1211,(1个2,1个1)
S(5)=111221,(1个1,1个2,2个1)
S(6)=312211,
规律一出来 ,纯水
#include<stdio.h> #include<string.h> int num[50]; char str[35][5000]; int Find(int t,char ss[]) { int i; for(i=t;ss[i];i++) { if(ss[i]!=ss[i+1]) return i; } return i; } void init() { strcpy(str[1],"1"); // printf("%s\n",str[1]); num[1]=1; for(int i=2;i<=30;i++) { int cas=0; for(int j=0;str[i-1][j];j++) { int t=Find(j,str[i-1]); int cnt=t-j+1; str[i][cas++]=cnt+'0'; str[i][cas++]=str[i-1][j]; j=t; } str[i][cas]='\0'; // printf("%s\n",str[i]); num[i]=strlen(str[i]); } } int main() { int n; init(); while(scanf("%d",&n)!=EOF) { if(n==0) break; printf("%d\n",num[n]); } return 0; }
G:这道题是我认为,新生赛中出的最好的,并没涉及算法,但是思路真的很不错,开阔了思维,隐藏的信息十分巧妙
现在给出我的理解,将所有数全部转换成二进制,进行一位一位的判断。
现假设8个数字分别为x1,x2。。。x8,总和为sum。
当8个数字的相同位上的数的1有奇数个(需考虑进位),那么无论这8个数字相加后依旧是奇数,所以此时若判断sum的同位置就可以,若sum的该位为0,则m的该位1,
若sum的该位0,则sum的该位为0;(假若进位的数是奇数,该位的1也是奇数,相加后是偶数,则说明该位的1本就是偶数个的)
由此推断:
1的个数偶数,sum同位为0,m的同位置为0,1的个数是1
sum同位为0,m的同位置为1,0的个数是1
奇数,sum同位为0,m的同位置为1,0的个数是1
sum同位为0,m的同位置为0,1的个数是1
关键是进位:若m为1,则进位的1,是1^其同一位置的0得到的,8-p就是0的个数
#include<stdio.h> #include<math.h> int main() { int T,i,sum; int x[9]; int ans[100]; scanf("%d",&T); while(T--) { for(i=0; i<8; i++) scanf("%d",&x[i]); scanf("%d",&sum); int p;//记录有几个1 int t=0;//记录需要进位的 int cas=0; int flag; while(1) { flag=0; p=0; for(i=0; i<8; i++) { if(x[i]&1) { p++; } if(x[i]!=0) flag=1; x[i]>>=1; } if(flag==0 && t==0) break; if((t+p)%2==0)//偶数 { if(sum&1)//sum为1,则sum该位为1 { t=(8-p+t)/2; ans[cas++]=1; } else { t=(t+p)/2; ans[cas++]=0; } } else { if(sum&1) { t=(t+p)/2; ans[cas++]=0; } else { t=(t+8-p)/2; ans[cas++]=1; } } sum>>=1; } int m=0,res=1; //printf("%d\n",cas); for(i=0; i<cas; i++) { m+=res*ans[i]; res*=2; } printf("%d\n",m); } return 0; }
H:求a窜中最多含多少个b窜,暴力~~~~
#include<stdio.h> #include<string.h> const int MAXN=1000010; char str1[MAXN],str2[10]; int Find(int t) { int cas=0,i; for(i=0;str2[i];i++) { if(str2[i]!=str1[t++]) return -1; } return i; } int main() { int T; int i,t; scanf("%d",&T); while(T--) { int ans=0; scanf("%s%s",str1,str2); int len2=strlen(str2); for(i=0;str1[i];i++) { if(str1[i]==str2[0]) { t=Find(i); if(t==len2) { ans++; i+=t-1; } } } printf("%d\n",ans); } return 0; }
I:这道题也很好,通过这道题,让我明白了,原来二分还可以这么用,数组a的下标记录的结果,而数组a的结果记录的输入值,是不是这个就是反哈希思路~~,二分也不单简简单 单的就是用”==“break,也可以让二分完。
还有就是memset不要乱用,造成MLE,由于hash数组开的大了也造成MLE,所以说一定要仔细分析题目,
#include<stdio.h> #include<string.h> const int MAXN=10000000; int a[MAXN],hash[10]; int tot; void init() { int i,j; tot=1; for(i=1;i<=10000000;i++) { j=i; memset(hash,0,sizeof(hash)); while(j) { if(hash[j%10]) break; hash[j%10]=1; j/=10; } if(j==0) a[tot++]=i; } } int main() { int n,res; init(); while(scanf("%d",&n)!=EOF) { if(n<1) {printf("0\n"); continue;} int begin=1; int end=tot-1; int mid; while(begin<=end) { mid=(begin+end)/2; if(a[mid]<n) { res=mid; begin=mid+1; } else end=mid-1; } printf("%d\n",res); } return 0; }
J: