HDU5573 2015 上海 B題(模擬-找規律)
/////// 2016.10.12
之前做有思路,時隔兩個月再做,思路都找不到了只是覺得題目有些熟,看來還是對這種解題思維沒有深入理解和學習,重溫。
而且發現了幾個點,之前確實考慮的不周全:
①構造題一定有明顯的地方告訴你一些資料上的限制規律,比如本題N<2^k<2^60,所以才考慮從由1 2 4 8...2^n這一條路上的數字下手,而不僅僅是因為二進位制可以表示任何數,否則無法解決走k層全部為加,之和卻還是小於N的情況。
②用cout輸出足夠大的數時,會自動用科學計數法,從而導致WA,最好的改善方法,就是直接用printf("%lld %c");
③注意迴圈變數i如果需要和N比較的話,那麼i也應該用Longlong,另外pow()函式,要在前面加long long 做一個強制型別轉換,printf("%lld %c",(long long)pow(...) );
④之前寫的程式碼可讀性太差了。重新在下面附上一份,重點在選擇哪些數為正哪些數為負時,其實只需將要減去的數由下到上遍歷一遍,如果之前所算的要減去的數(指 差/2)大於目前節點的數,那麼目前節點輸出負號,要減去的數減去目前節點的數之後繼續往上遍歷即可。因為同樣的道理,差/2 也是可以由這一條路上的數字的某個組合組成。
///////2016.08.10
0
題目很簡單
1
思路分析:
因為是special判題,所以不止一種可能。
其次,關鍵在於這樣一句:N<2^k<2^60,所以如果每層選中的點要麼加 要麼不加也不減,那麼N一定可以由1 2 4 8...2^n這一條路上的數字組成(類似二進位制可以表示任何數)
再考慮如果有減,就相當於將這裡一條路上的數字都加起來然後選擇其中部分數為減,使得最終結果等於N。
考慮n1 2 4 8 ... 2^相加之和,減去路上的某一個數就相當於總和減去這個數兩遍,那麼減去的數之和為之前相差的一半即可,最後多減的1使得2^n這個數為2^n+1即可
以10 4為例子,1 2 4 8 ,之和為15,15-10=5,也就是說減去5的一半,(因為題目要求非加即減,那麼減去任意一個數,都相當於所有數的和減去該數兩遍),而5%2==1時,不是偶數,我們將5+1=6,6/2=3,從前面的數總共減去3即可,而多減去的1,用最後一位8+1=9來彌補即可抵消。
以 9 4為例,1 2 4 8,之和為15,15-9=6,是偶數,所以6/2=3,直接從前面減去得數之和為3即可。(注意,前面的數,總是2的x次方)
做的時候,思路是有的但是不是特別清晰,另外模擬題還是考驗編碼能力。
2
之前的程式碼:
#include <iostream>
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
long long int n;
int k;
int cou;
long long int a[65];
int ttt=0;
int Find(int rr){
int i=1;
int flagg=0;
while(i<=rr){
if(i==rr){
flagg=1;
}
i*=2;
}
return flagg;
}
int main(){
int kase;
cin>>kase;
for(int kk=1;kk<=kase;kk++){
cou=0;
ttt=0;
scanf("%lld%d",&n,&k);
long long int zh=1;
long long int gg=1;
for(int i=2;i<=k;i++){
zh+=(gg*2);
gg=gg*2;
}
long long int yu=zh-n;
printf("Case #%d:\n",kk);
if(yu==0){
for(int i=1;i<=zh;i=i*2){
cout<<i<<" "<<"+"<<endl;
}
}
else{
if(yu%2==1){
yu++;
ttt=1;
yu/=2;
}
else{
yu/=2;
}
while(!Find(yu)){
int hh=0;
int i=1;
while(hh==0){
if(i>yu){
i/=2;
a[cou++]=i;
yu=yu-i;
hh=1;
}
i*=2;
}
}
a[cou++]=yu;
int pp=1;
for(int l=1;l<=k;l++,pp=pp*2){
if(ttt==1&&l==k){
pp+=1;
}
if(cou>=0&&pp==a[cou-1]){
cou--;
cout<<a[cou]<<" "<<"-"<<endl;
}
else{
cout<<pp<<" "<<"+"<<endl;
}
}
}
}
}
之後的程式碼:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include <ctime>
#include<queue>
#include<set>
#include<map>
#include<list>
#include<stack>
#include<iomanip>
#include<cmath>
#include<bitset>
#define memset(ss,b) memset((ss),(b),sizeof(ss))
///#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long ll;
typedef long double ld;
#define INF (1ll<<60)-1
#define Max 1e9
using namespace std;
int k;
int n;
int t;
char a[70];
int flag=0;
ll num[65];
int main(){
scanf("%d",&t);
int tt=1;
num[0]=1;
num[1]=2;
for(int i=2;i<=60;i++){
num[i]=2*num[i-1];//放在外面先算一遍
}
while(t--){
scanf("%d%d",&n,&k);
printf("Case #%d:\n",tt++);
memset(a,0);
ll sum=0;
ll yushu=0;//注意用long long
for(int i=0;i<k;i++){
sum+=num[i];
}
yushu=sum-n;
int ji=0;//需要補償+1的標誌
if(yushu%2==0){
yushu/=2;
}
else if(yushu%2==1){
yushu+=1;
yushu/=2;
ji=1;
}
for(int i=k;i>=1;i--){
if(yushu>=num[i-1]){
yushu-=num[i-1];
a[i]='-';
}
else{
a[i]='+';
}
}
for(int i=1;i<k;i++){
printf("%lld %c\n",num[i-1],a[i]);
}
if(ji) printf("%lld %c\n",num[k-1]+1,a[k]);
else printf("%lld %c\n",num[k-1],a[k]);
}
return 0;
}