[2010年江苏省选拔赛第三轮 第一试 找零钱的洁癖]BFS、Hash、贪心

【题目大意】
给出N个数和一个X,问这些数最少用多少个加加减减可以得到X。
X<=0x7FFFFFFF
【算法分析】
显然,这是搜索题。
然后。。。我的是cheat的。
一开始我是写双向广搜,貌似没多少分。
然后我同学直接单向暴力:80分。。。
好吧,沿用这个单向暴力广搜,然后发现TLE的两个点的数据都是由2^0 2^1 2^2….. 2^32组成的。
然后就是从2^32一直弄下来贪心就行了。。。就是二进制
下面的BFS,爆队列的话,直接就return INF。
然后。。。这样就可以弱弱地AC了。
【其它】
求正解。。。
【CODE】
#include using namespace std;
typedef long long big;
const big N=1000003;
const big INF=0x7FFFFFFF;
struct link{big y,next;}g[N];
big e,ans,n;
big ls[N],step[N];
big list[N],X,a[N];

void init(){
e=0; memset(ls,0,sizeof(ls));
cin >> X;
n=0;
while (cin >> a[n]) n++;
sort(a,a+n);
}

big greedy(big mb){
big times=0;
for (big i=n-1;i>=0;i–){
times+=mb/a[i];
mb-=mb/a[i]*a[i];
}
if (mb) return INF;
return times;
}

big inhash(big k){
big key=(k+INF)%N;
for (big t=ls[key];t;t=g[t].next)
if (list[g[t].y]==k) return g[t].y;
return 0;
}

void ins(big zz){
e++;
big key=(big)((list[zz]+INF)%N);
g[e].y=zz;
g[e].next=ls[key];
ls[key]=e;
}

big bfs(){
if (X==0) return 0;
big head=0,tail=1,i,pos;
list[1]=0; step[1]=0;
while (head head++;
for (i=0;i tail++;
if (tail>=N-22) return INF;
step[tail]=step[head]+1;
if (list[head] else list[tail]=list[head]-a[i];
if (list[tail]==X) return step[tail];
pos=inhash(list[tail]);
if (pos) {tail–; continue;}
ins(tail);
}
}
}

int main(){
init();
ans=greedy(X);
big tmp=bfs();
ans=min(ans,tmp);
cout << ans << endl;
}

[BZOJ1041 [HAOI2008]圆上的整点]数论、勾股数相关定理

【题目地址】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1041
【算法分析】
以下内容来自师傅AekdyCoin的blog以及口授
现在给定了一个方程a^2+b^2=c^2  (c已知)
这,就是勾股数。
然后勾股数有一个很霸气的定理。
1、a=m^2-n^2
2、b=2*m*n
3、c=m^2+n^2
4、gcd(n,m)=1。
5、gcd(a,b,c)=1。
同时满足这五条式的是一组勾股数,而且对于所有满足这五条式的(m,n)乘一个k(k>=1),即(km,kn)。就可以表示所有的勾股数,并且勾股数和三元对(m,n,k)一一对应。
换句话说,每个勾股数都只能表示为一个三元对(m,n,k)。

好了,现在回到这题的算法上。
我们枚举k^2(程序中是变量:div),但是k这里有一个小技巧,并不需要枚举1<=k^2<=r,
只要枚举1<=k^2<=sqrt(r)即可。因为我们可以保证拆成的两个乘数的大小顺序。
然后枚举了k^2以后,
问题转化成能不能找到一对二元对(m,n),满足:
1、gcd(m,n)=1
2、m^2+n^2==r/(k^2)
3、gcd(m^2-n^2,2*m*n,m^2+n^2)=1
然后这样暴力还是会很慢的,于是师傅就把第三个条件直接抽出来先判断。
我们可以从:1、3两式推出  r%4=1!
于是直接预先判断,里面那个复杂度为O(sqrt(r))的部分就很少会执行了!
然后算法甚至达到了接近O(sqrt(r))的复杂度。

【其它】
时间到了0MS。空间也没用什么。
膜拜AekdyCoin大神。。。
【CODE】
#include #include using namespace std;
int ans=0;

int gcd(int a,int b){return b?gcd(b,a%b):a;}

void solve(int r){
if (r==1 || r%4!=1) return;
int m,n;
for (n=1;n*n*2 m=(int)(sqrt(r-n*n)+1e-5);
if (m*m+n*n!=r) continue;
if (gcd(m,n)==1 && m>n) ans++;
}
}

int main(){
int r,div;
cin >> r;
ans=0;
for (div=1;div*div<=r;div++){
if (r%div) continue;
solve(r/div);
solve(div);
}
ans*=8;
ans+=4;
cout << ans << endl;
}

ZOJ比赛——2010.4.17

本次比赛由我和CZM一起参加。
先贴提交记录:
Run ID Submit Time Judge Status Problem ID Language Run Time(ms) Run Memory(KB) User Name 3470 2010-04-17 16:19:45 Accepted K C++ 130 220 CWJ&&CZM 2991 2010-04-17 15:35:34 Accepted J C++ 890 13752 CWJ&&CZM 1496 2010-04-17 13:46:15 Accepted E FPC 20 28 CWJ&&CZM 1188 2010-04-17 13:28:52 Wrong Answer E FPC 20 28 CWJ&&CZM 1085 2010-04-17 13:20:44 Accepted G C++ 0 176 CWJ&&CZM 596 2010-04-17 12:50:55 Accepted B C++ 0 176 CWJ&&CZM 463 2010-04-17 12:45:57 Accepted L C++ 0 176 CWJ&&CZM 311 2010-04-17 12:41:33 Accepted A C++ 0 176 CWJ&&CZM
首先一开场,CZM没在,于是我AC3道水题:A、B、L。
然后CZM上了,然后兵分两路,他做E,我做K。
然后他说E题十分水。
然后我看了一下K以后,发现和SGU122很像,但是又略有不同,然后想留着后面做算了。
于是我看到G题,一大堆英文和五行的,什么都没看懂。
绝望之际,随便交了个n/2,居然AC了!
。。。十分无语
然后CZM的E题WA了一次,然后还是AC了。
于是我开始YY J题。
一看。。。恩,感觉应该是以i划分状态,然后f[i,j,k]表示第i个完成,A处理器末时间为j,B处理器末时间为k的时候能否做到。当我看到我的f是布尔型的时候,觉得有些搞笑。。。于是换成f[i,j]表示第i个任务完成,A处理器末时间为j,B处理器末时间最小能是多少。
然后将sum猥琐优化一下,1Y,不过这个时间确实挺难看。。。
然后我们两个最后再来搞K题,讨论了半天,CZM觉得他的构造法是对的,我表示没听懂~
我想的是费用流,写了没几行,突然想起费用流不能有环。。。
于是我想用度数预处理一下暴力,CZM继续做他的构造法。
然后我的AC,他用小号交WA了。。。
我的做法是:选取出度最大的一个点做起点,然后DFS即可。
因为图是接近完全图,所以搜出来也很正常。。。
然后,我想暴力C题,最后没时间,比赛结束。
最后我们过了7题。
rank:22
排名围观地址:http://acm.zju.edu.cn/onlinejudge/showContestRankList.do?contestId=308

现在说说AC的两道不算太弱智的题目,现在贴贴简单的解题报告和程序:
J:F[i,j]表示完成第i个任务,A处理器末时间为j时,B处理器末时间最小为多少。然后转移即可。
K:由于每个点的出度+入度=n-1,于是把出度最大的那个当起点,暴力DFS即可。当然也可以DLX。
【CODE For J】
#include using namespace std;
const int N=105;
const int INF=100000000;
int Tc,n;
int a[N],b[N],tail[N];
int F[N][N*N];
int list[N][N*N*2];

void init(){
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d%d",&a[i],&b[i]);
}

int solve(){
int i,j,k,sum=0,*f;
a[n+1]=0;
for (i=1;i<=n+1;i++){
sum+=a[i];
for (j=0;j<=sum;j++)
F[i][j]=INF;
}
F[1][0]=0;
sum=0;
for (i=1;i<=n;i++){
f=F[i+1];
for (j=0;j<=sum;j++)
if (F[i][j]!=INF){
f[j+a[i]]=min(f[j+a[i]],max(F[i][j],j));
f[max(j,F[i][j])]=min(f[max(j,F[i][j])],F[i][j]+b[i]);
}
sum+=a[i];
}
int ans=INF;
for (j=0;j<=sum;j++)
ans=min(ans,max(j,F[n+1][j]));
return ans;
}

int main(){
scanf("%d",&Tc);
for (int i=1;i<=Tc;i++){
init();
cout << solve() << endl;
}
}

【CODE For K】
#include #include #include const int N=105;
int n,S,step;
int din[N],dout[N],a[N][N],v[N],list[N];
bool flag;

void init(){
scanf("%d",&n);
memset(din,0,sizeof(din));
memset(dout,0,sizeof(dout));
memset(a,0,sizeof(a));
int i,j,x,y;
for (i=1;i<=n*(n-1)/2;i++){
scanf("%d%d",&x,&y);
a[x][y]=1;
din[y]++;
dout[x]++;
}
}

void dfs(int i){
v[i]=1;
step++;
list[step]=i;
if (step==n){
flag=true;
return;
}
for (int j=1;j<=n;j++)
if (a[i][j] && !v[j]){
dfs(j);
if (flag) return;
}
step–;
v[i]=0;
}

void work(){
if (n==1){
printf("1n");
return;
}
S=1;
for (int i=1;i<=n;i++)
if (dout[i]>dout[S]) S=i;
memset(v,0,sizeof(v));
flag=false;
step=0;
dfs(S);
if (!flag) printf("Impossiblen");
else
for (int i=1;i<=n;i++){
printf("%d",list[i]);
if (i!=n) printf(" ");
else printf("n");
}
}

int main(){
int Tc;
scanf("%d",&Tc);
for (int i=1;i<=Tc;i++){
init();
work();
}
}

[SSLOJ1366 正方矩阵]二分答案+排序、利用RMQ思想达到比较的O(1)

【题目大意】
在一个N*M 的字母矩阵中寻找一个正方子矩阵, 该正方矩阵在原矩阵中重复出现至少K次. 这些正方子矩阵可以出现局部的重叠,但不能是完全的重叠.
输入给定N,M,K和这个矩阵。
输出满足条件的最大的正方子矩阵的边长, 如果没有正方子矩阵能满足条件,输出0.
【算法分析】
显然答案满足单调性。二分答案。
然后怎么判断呢?
我们可以通过预处理边长为2^i的正方形的排名,然后利用这个排名,四分需要的正方形空间。
然后利用n+1进制表示出来,作为他的权值。显然,对于同一个矩阵,权值是一定的。
而对于不同的矩阵,权值是一定不同的。于是达到的O(1)的比较。
所以最终算法复杂度O(N^2 lg^2 N)

然后听说还有另外一种方法是用hash的?
【其他】
跑得比较慢= =。。。
【CODE】
#include #include #include #include #define ra rank[lg[len]]
using namespace std;
const int N=505;
typedef long long lld;
struct PT{int x,y;}a[N*N];
int m,n,appear,len,ns,tx,ty;
char s[N][N];
int lg[N],rank[14][N][N];
lld lltmp,res;

inline void input(){
scanf("%d%d%d",&m,&n,&appear);
for (int i=1;i<=m;i++)
for (int j=1;j<=n;j++){
char ch=getchar();
while (ch==’ ‘ || ch==’n’) ch=getchar();
s[i][j]=ch;
}
}

inline lld count (PT *TMP){
res=0;
int &x=TMP->x,&y=TMP->y;
ns=1< res=ra[x][y]*(n+1);
if (tx<=m) res+=ra[tx][y];
res*=n+1;
if (ty<=n) res+=ra[x][ty];
res*=n+1;
if (tx<=m && ty<=n) res+=ra[tx][ty];
return res;
}

inline int cmp0(const void *x,const void *y){
return s[((PT *)x)->x][((PT *)x)->y]-s[((PT *)y)->x][((PT *)y)->y];
}

inline int cmps(const void *X,const void *Y){
lltmp=count((PT *)X)-count((PT *)Y);
if (lltmp>0) return 1;
if (lltmp<0) return -1;
return 0;
}

inline void init(){
int i,j,tmp,step;
lg[1]=0;
for (i=2;i<=max(n,m);i++){
lg[i]=lg[i-1];
if (1<<(lg[i]+1)==i-1) lg[i]++;
}

for (tmp=0,i=1;i<=m;i++)
for (j=1;j<=n;j++){
a[++tmp].x=i;
a[tmp].y=j;
}
qsort(a+1,m*n,sizeof(PT),cmp0);
for (tmp=0,i=1;i<=m*n;i++){
if (i==1 || s[a[i].x][a[i].y]!=s[a[i-1].x][a[i-1].y]) tmp++;
rank[0][a[i].x][a[i].y]=tmp;
}

for (step=1;step<=lg[max(n,m)];step++){
len=1< for (tmp=0,i=1;i<=m;i++)
for (int j=1;j<=n;j++){
a[++tmp].x=i;
a[tmp].y=j;
}
qsort(a+1,m*n,sizeof(PT),cmps);
for (tmp=0,i=1;i<=m*n;i++){
if (i==1 || count(&a[i-1])!=count(&a[i])) tmp++;
rank[step][a[i].x][a[i].y]=tmp;
}
}
}

inline int cmp(const void *x,const void *y){
lltmp=count((PT *)x)-count((PT *)y);
if (lltmp>0) return 1;
if (lltmp<0) return -1;
return 0;
}

inline bool solve(int NowAns){
int i,j,tmp,nL;
len=NowAns;
for (tmp=0,i=1;i<=m;i++)
for (j=1;j<=n;j++){
a[++tmp].x=i;
a[tmp].y=j;
}
qsort(a+1,m*n,sizeof(PT),cmp);
for (nL=1,i=2;i<=m*n;i++){
if (count(&a[i])!=count(&a[i-1])) nL=1;
else nL++;
if (nL>=appear) return true;
}
return false;
}

int main(){
input();
init();
int l=0,r=min(n,m),mid;
while (l+1 mid=(l+r)/2;
if (solve(mid)) l=mid;
else r=mid-1;
}
if (solve(r)) l=r;
printf("%dn",l);
}

[SSLOJ1362 指纹]各种猥琐预处理+树状数组

【题目大意】
有N个程序,每个数据有4个测试项。
给出每个程序在这4个测试项里的排名,(排名保证取遍1..N的每个数),排名越前越牛X。
然后假设对于某一条程序,存在另外一条程序在4项中,有>=3项比它优,那么它就成为了SB程序。
问有多少个SB程序。
【算法分析】
先枚举地移走某一项,然后剩下的三项一起弄。(移走的那项暂时忽略,因为只需>=3个比它优)
这三项又可以按某一项排序,然后问题就变成了将给出N个二元对(Ai,Bi),然后对于每个二元对,判断有没有出现在它之前的,且满足Ai然后我们可以以Ai为key值作树状数组的下标,然后就可以在lgN的复杂度内维护Ai<=K的点中,Bi最小是多少。然后再标记一下,问题就可以解决了。
【CODE】
#include #include #include const int N=100005;
int n;
int a[N][5],d[N][5],Q[N];
bool bad[N];

inline int cmp(const void *x,const void *y){return ((int *)x)[0]-((int *)y)[0];}

void solve(){
memcpy(d,a,sizeof(int)*5*(n+1));
qsort(d+1,n,sizeof(int)*5,cmp);
memset(Q,50,sizeof(int)*(n+1));
int i,res,j;
for (i=1;i<=n;i++){
res=0x7FFFFFFF;
j=d[i][1]-1;
while (j){
res j-=j&-j;
}
if (res j=d[i][1];
while (j<=n){
Q[j] j+=j&-j;
}
}
}

int main(){
scanf("%d",&n);
int i,j,k,tmp;
for (i=1;i<=n;i++){
a[i][4]=i;
for (j=0;j<4;j++)
scanf("%d",&a[i][j]);
}
for (k=0;k<4;k++){
for (i=1;i<=n;i++){
tmp=a[i][k]; a[i][k]=a[i][3]; a[i][3]=tmp;
}
solve();
for (int i=1;i<=n;i++){
tmp=a[i][k]; a[i][k]=a[i][3]; a[i][3]=tmp;
}
}
int ans=0;
for (int i=1;i<=n;i++)
if (bad[i]) ans++;
printf("%dn",ans);
for (int i=1;i<=n;i++)
if (bad[i]) printf("%dn",i);
}