fjnu09暑期训练赛题解

July 30th, 2010 PhoenixWright 10 comments

题目地址密码:000111
1001Who’s in the Middle
简单题,求中位数

#include <iostream>
#include <algorithm>
using namespace std;
int a[10004];
int main() {
    int n;
    while(scanf("%d", &n) != EOF) {
    for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
        sort(a, a+n);
        printf("%d\n", a[n/2]);
    }
    return 0;
}

1002跑跑卡丁车
每过一段赛道,可以得到20%能量,达到100%换一个加速卡,最多2张加速卡,有2个加速卡而能量再次集满,那么能量清零但得不到加速卡,那么可知总共有15种状态:

加速卡数量   0   0   0   0   0   1   1   1   1   1   2   2   2   2   2
能量        0% 20% 40% 60% 80%  0% 20% 40% 60% 80%  0% 20% 40% 60% 80%
状态         0   1   2   3   4   5   6   7   8   9  10  11  12  13  14

我们设v[i](0<=i<=14)代表在到达状态i时,所用的最短时间.
假设普通用时为a[i],加速用时为b[i],如果我们不用加速卡,
那么有v[i] = v[i-1]+a[i],这里还要注意到一点,就是当现在处于状态14时,如果仍旧不用加速卡而保持普通速度行驶一段,则能量清0,加速卡仍旧为2张,即回到状态10,所以有
v[10] = v[14]+a[i]
对于用了加速卡的情况,则有
v[i-5] = v[i]+b[i](i>=5)
最后,注意到总共可能要行驶100*100段,而每一段的状态又只与前一段有关,所以为了节约空间,采用轮转数组。

#include<iostream>
using namespace std;
const int M = 15;
int main() {
    int l, n;
    int a[101], b[101], t = 0;
    unsigned int v[2][M], res;
    while(scanf("%d%d", &l, &n) != EOF) {
        for(int i = 0; i < l; ++i) scanf("%d", &a[i]);
        for(int i = 0; i < l; ++i) scanf("%d", &b[i]);
        memset(v, 0x0f0f, sizeof(v));
        res = 0x0f0f0f0f;
        v[t][0] = 0;
        for(int i = 0; i < n; ++i)
            for(int j = 0; j < l; ++j) {
                v[!t][0] = 0x0f0f0f0f;
                for(int k = 0; k < M; ++k) {
                    if(k != M - 1) v[!t][k+1] = v[t][k] + a[j];
                    if(k >= 5 && v[!t][k-5] > v[t][k] + b[j])
                        v[!t][k-5] = v[t][k] + b[j];
                }
                //有2个加速卡而能量再次集满的情况如下
                if(v[!t][10] > v[t][14] + a[j])
                    v[!t][10] = v[t][14] + a[j];
                t = !t;
            }
        for(int i = 0; i < M; ++i)
            if(res > v[t][i]) res = v[t][i];
        printf("%u\n", res);
    }
    return 0;
}

1003 Rescue
BFS+优先队列
给定一个地图,里面有很多个r,一个a,求这么多个r里,哪个能最快走到a,输出最短时间,如果中间遇到x,则时间要+1
我们可以换个思路,从a出发,去找离它最近的r,这样就可以用BFS解决,又因为遇到x要+1,普通的队列会得不到最优值,所以用优先队列

#include <iostream>
#include <queue>
using namespace std;
int h, w, res;
int dx[] = {0, -1, 0, 1};
int dy[] = {1, 0, -1, 0};
char mp[202][202];
struct P {
    int x, y, v;
    bool operator<(const P &a) const{
        return v > a.v;
    }
    P(int xx, int yy, int hh) {
        x = xx, y = yy, v = hh;
    }
    P(){}
}tmp;
int bfs() {
    priority_queue<P>Q;
    for(int i = 0; i < h; ++i)
    for(int j = 0; j < w; ++j)
        if(mp[i][j] == 'a') {
            Q.push(P(i, j, 0));
            goto t1;
        }
t1:;
    while(!Q.empty()) {
        tmp = Q.top();
        Q.pop();
        for(int i = 0; i < 4; ++i) {
            int tx = tmp.x + dx[i], ty = tmp.y + dy[i];
            if(tx >= 0 && tx < h && ty >= 0 && ty < w && mp[tx][ty] != '#') {
                if(mp[tx][ty] == '.') Q.push(P(tx, ty, tmp.v+1));
                else if(mp[tx][ty] == 'x') Q.push(P(tx, ty, tmp.v+2));
                else if(mp[tx][ty] == 'r') return tmp.v + 1;
                mp[tx][ty] = '#';
            }
        }
    }
    return -1;
}
 
int main() {
    while(scanf("%d%d", &h, &w) != EOF) {
        for(int i = 0; i < h; ++i) scanf("%s", mp[i]);
        res = bfs();
        if(res == -1) puts("Poor ANGEL has to stay in the prison all his life.");
        else printf("%d\n", res);
    }
    return 0;
}

1004统计难题
简单Tire树应用

#include<iostream>
using namespace std;
struct Tire {
	Tire *a[26];
	int cnt;
}tire[1000000], *q;
int l = 1;
int main() {
	char tmp[13];
	int s, i;
	while(gets(tmp)) {
		if(tmp[0] == '\0') break;
		q = &tire[0];
		for(i = 0; tmp[i] != '\0'; ++i) {
			if(q->a[tmp[i]-'a'] == NULL) q->a[tmp[i]-'a'] = &tire[l++];
			q = q->a[tmp[i]-'a'];
			++q->cnt;
		}
	}
	while(gets(tmp)) {
		q = &tire[0];
		for(i = 0; tmp[i] != '\0'; ++i) {
			if(q->a[tmp[i]-'a'] == NULL) break;
			q= q->a[tmp[i]-'a'];
		}
		if(tmp[i] == '\0') printf("%d\n", q->cnt);
		else puts("0");
	}
	return 0;
}

1005Climbing Worm
就是跟着题目意思模拟就行了。。

#include <iostream>
using namespace std;
int main() {
    int n, u, d, i, c, t;
    while(scanf("%d%d%d", &n, &u, &d)) {
		if(n == 0) break;
		c = t = 0;
        while(c < n) {
            ++t;
            if(t & 1) c += u;
            else c -= d;
        }
        printf("%d\n", t);
    }
    return 0;
}

有无人回复哇o_o

Categories: ACM, Algorithm Tags:

关于连接池配置的一些事

April 17th, 2010 PhoenixWright 1 comment

今天试着配置了下tomcat的连接池,这里我用的是mysql数据库
首先要把jdbc的jar扔到tomcat目录下的lib以及自己工程的lib里,如果不放到tomcat下,会提示说找不到驱动.
然后在工程的META-INF里新建一个context.xml,内容如下

<Context docBase="onlinejudge">
 
   <Resource name="jdbc/FjnuDB" auth="Container" type="javax.sql.DataSource"
               maxActive="500" maxIdle="50" maxWait="20000"
               username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
			   removeAbandoned="true" removeAbandonedTimeout="60" logAbandoned="true"
               url="jdbc:mysql://localhost:3306/onlinejudge?autoReconnect=true"/>
 
</Context>

其中,username跟password很明显,代表连接数据库的用户名与密码,在url里面的onlinejuge则是数据库.
maxActive连接池允许的最大并发连接数,值为非正数时表示不限制。
maxIdle连接池中的最大空闲连接数,超过此数值时多余的空闲连接将会被释放,值为负数时表示不限制。
maxWait以毫秒表示的当连接池中没有可用连接时等待可用连接返回的时间,超时则抛出异常,值为-1时无限期等待。
driverClassName使用的JDBC驱动完整JAVA类名。
removeAbandoned 是否清除已经超过“removeAbandonedTimout”设置的无效连接。如果值为“true”则超过“removeAbandonedTimout”设置的无效连接将会被清除。设置此属性可以从那些没有合适关闭连接的程序中恢复数据库的连接。
removeAbandonedTimeout以秒表示的清除无效连接的时限。
最后这两个值最好要设定,当我们编程时忘记将连接还给连接池时,设置这两个选项将会自动回收,当然最好还是要在程序中显式的回收
其中name=”jdbc/FjnuDB”这个是我们命名的,下面将要用到这个名字
在工程的WEB-INF/web.xml里添加如下代码:

  <resource-ref>
    <description>DB Connection</description>
    <res-ref-name>jdbc/FjnuDB</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
  </resource-ref>

注意这里的就是我们在刚才name=”"里填写的名字
然后在JAVA中可以这样调用

DataSource ds = null;
			InitialContext localInitialContext;
			try {
				localInitialContext = new InitialContext();
				ds = (DataSource)localInitialContext.lookup("java:/comp/env/jdbc/FjnuDB");
			} catch (NamingException e) {
				logger.error("NamingException:", e);
			}
return ds.getConnection();

注意,这里的context.xml其实在实际运行中好像只在第一次会将其复制到tomcat目录下的conf\Catalina\localhost里,并命名成你工程的相应名字,如我的工程是oj,则名字为oj.xml,然后以后读取内容,实际都是从该文件读取,如果你后面修改了context.xml的配置,必须将oj.xml删掉或者手动复制一份新的,否则将继续用旧的配置,我今天为这事就弄了好久才发现原来是这么回事……

Categories: OJ Develop Tags: ,

FJNU_2010纳新赛

April 11th, 2010 PhoenixWright 7 comments

比赛地址
题目不分难易,即使题目很简单,不同的人也会有不同的做法,多看别人的程序,可以拓宽自己的思路
A:Phoenix的果园之再度归来
考察对单调队列的应用
以样例1 3 5 2 99 23 8 23 0 2来说,已知k=3,假设我们要求的是最小值,那么我们要维护一个单调递增的队列,同时记录每个元素的位置,一开始前3个元素都刚好递增,所以可以全部插入得到队列
1 3 5
0 1 2
下面是它们的位置,于是对于区间[0,2],其最小值就是1,接下来我们要插入第3个元素,为了保持单调递增的性质,则必须把3,5舍去,于是得
1 2
0 3
于是对于区间[1,3],因为第0个元素不在该区间内,所以舍去,所以该区间最小值为2,接下来插入第4个元素,由于99比2大,所以可以直接插入,得到(注意第0个元素已舍去)
2 99
3 4
于是对于区间[2,4],由于最头的元素2在该区间内,不用舍去,所以该区间最小值为2.
如此演算下去,求最大值的时候,换成单调递减的对列即可,所以最终复杂度为O(n)

#include <iostream>
using namespace std;
int a[1000002];
// true min, false max
#define C1(a, b) ((a)>(b))
#define C2(a, b) ((a)<(b))
#define cmp(a, b, c) (c) == true ? C2(a, b) : C1(a, b)
struct M {
	int f, t, s[1000002][2];
	bool fl;
	M(bool tt) {f = t = 0, fl = tt;}
	void reset() {f = t = 0;}
	M() {}
	void ins(int v, int idx) {
		while(f != t && !(cmp(s[t-1][0], v, fl)))
			--t;
		s[t][0] = v, s[t++][1] = idx;
	}
	int del(int idx) {
		while(s[f][1] < 0idx) ++f;
		return s[f][0];
	}
} Min(true), Max(false);
int main() {
	int n, k, i, j;
	while(scanf("%d%d", &n, &k) != EOF) {
		Min.reset(), Max.reset();
		for(i = 0; i < n; ++i) scanf("%d", &a[i]);
		for(i = 0; i < k; ++i) Min.ins(a[i], i), Max.ins(a[i], i);
        printf("%d", Max.del(0));
		for(i = 1, j = k; i + k <= n; ++i, ++j) {Max.ins(a[j], j); printf(" %d", Max.del(i));}
		printf("\n%d", Min.del(0));
		for(i = 1, j = k; i + k <= n; ++i, ++j) {Min.ins(a[j], j); printf(" %d", Min.del(i));}
		puts("");
	}
	return 0;
}

B:数鸭子
对于某个m*n的矩阵mat,我们要先进行预处理,使得对于某个位置mat[i][j]的值,代表以0,0到i,j为对角线组成的矩形里的所有元素的和,比如对于矩阵
1 2
3 4
经过预处理必须变为
1 3
4 10
那么对于某个询问,我们假设所给的两点为从左上到右下的对角线,其中(a,b)代表左上的点,(c,d)代表右下的点,那么该对角线构成的矩形里元素的和为
mat[c][d] – mat[a-1][d] – mat[c][b-1] + mat[a-1][b-1]
经过预处理后,对于每次询问,即可在O(1)的复杂度下算出。
注意,题目给定的询问并不是一定是为从左上到右下的对角线,所以要经过一定的处理转换成从左上到右下的对角线,以方便统一处理

#include <stdio.h>
int s[1002][1002];
#define SWAP(a,b) ((a)^=(b)^=(a)^=(b))
int cal(int a, int b, int c, int d) {
    if(a > c) {
        SWAP(a, c);
        SWAP(b, d);
    }
    if(b > d) SWAP(b, d);
	return s[c][d] - s[a-1][d] - s[c][b-1] + s[a-1][b-1];
}
int main() {
	int a, b, c, d, t, i, j, n, m, k;
    scanf("%d", &t);
	while(t--) {
        scanf("%d%d", &m, &n);
        for(i = 1; i <= m; ++i) {
            for(j = 1; j <= n; ++j) scanf("%d", &s[i][j]);
            for(j = 1; j <= n; ++j) s[i][j] += s[i][j-1];
        }
		for(i = 1; i <= m; i++)
			for(j = 1; j <= n; j++) s[i][j] += s[i-1][j];
        scanf("%d", &k);
        while(k--) {
            scanf("%d%d%d%d", &a, &b, &c, &d);
            printf("%d\n", cal(a, b, c, d));
        }
        puts("");
	}
}

C:Axis Bunker
由于最多只有4*4的大小,我们只要暴力搜索,对于每个没有建筑物的地方,一一试放,判断是否与已放的枪冲突即可

#include <stdio.h>
int n, ans;
char mp[5][5];
int check(int i, int j) {
    int it = i, jt = j;
    while(it >= 0 && mp[it][j] == '.') --it;
    if(it >= 0 && mp[it][j] != 'X') return 0;
    while(jt >= 0 && mp[i][jt] == '.') --jt;
    if(jt >= 0 && mp[i][jt] != 'X') return 0;
    return 1;
}
void dfs(int i, int j, int t) {
    int it = i, jt = j + 1;
    if(jt == n) ++it, jt = 0;
    if(it == n) {
        if(mp[i][j] != 'X' && check(i,j)) ++t;
        if(ans < t) ans = t;
        return;
    }
    if(mp[i][j] == 'X') dfs(it, jt, t);
    else if(check(i, j)) {
        mp[i][j] = 'Y';
        dfs(it, jt, t+1);
        mp[i][j] = '.';
        dfs(it, jt, t);
    } else dfs(it, jt, t);
}
int main() {
    int i;
    while(scanf("%d", &n)) {
        if(n == 0) break;
        for(i = 0; i < n; ++i) scanf("%s", mp[i]);
        ans = 0;
        dfs(0, 0, 0);
        printf("%d\n", ans);
    }
    return 0;
}

D:Calling
这题由于数据量的关系,可以直接一个一个暴力匹配过去,但是正解是把这每个二进制串转成十进制,通过一个数组来判断出现次数

#include <stdio.h>
short a[401];
int C(char *p) {
    char *q;
    int i, v = 0;
    for(q = p, i = (1<<9); *q; ++q, i >>= 1)
        if(*q == '1') v += i;
    return v;
}
int main() {
    int n1, n2, t, i;
    char buf[11];
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n1);
        for(i = 0; i < n1; ++i) {
            scanf("%s", buf);
            a[i] = C(buf);
        }
        int v[1025] = {0};
        scanf("%d", &n2);
        for(i = 0; i < n2; ++i) {
            scanf("%s", buf);
            ++v[C(buf)];
        }
        for(i = 0; i < n1; ++i) printf("%d\n", v[a[i]]);
        puts("");
    }
}

E:黑白棋
这其实是一道简单的模拟题,只要对于每个没有放棋的位置,对其试放黑棋,然后判断其八个方向是否存在两个黑棋中间全是白棋的情况,如果存在,将这些白棋数计下,对于每个位置,一一测试即可取最大值即可

#include <stdio.h>
char mp[9][9];
int dh[] = {-1, -1, 0, 1, 1, 1, 0, -1};
int dw[] = {0, 1, 1, 1, 0, -1, -1, -1};
int check() {
    int t1, t2, ans = 0, i, j, it, jt, k;
    for(i = 0; i < 8; ++i)
        for(j = 0; j < 8; ++j) if(mp[i][j] == '*') {
            t1 = t2 = 0;
            for(k = 0; k < 8; ++k) {
                it = i, jt = j, t1 = 0;
                do {
                    it += dh[k], jt += dw[k];
                    ++t1;
                } while(it >= 0 && it < 8 && jt >= 0 && jt < 8 && mp[it][jt] == 'L');
                if(it >= 0 && it < 8 && jt >= 0 && jt < 8 && mp[it][jt] == 'D') t2 += t1 - 1;
            }
            if(t2 > ans) ans = t2;
        }
    return ans;
}
int main() {
    int n, i, t;
    scanf("%d", &n);
    for(t = 1; t <= n; ++t) {
        for(i = 0; i < 8; ++i) scanf("%s", mp[i]);
        printf("Case %d: %d\n", t, check());
    }
}

F:统计数字
由于每个数字不超过150000,所以简单的开一个150001大小的数组来存放每个数字出现的次数,最后将次数不为0的数字输出即可
PS:如果数字很大,达到上亿的话,那么就要考虑通过hash来解决。后面写个hash的解决方法

#include <stdio.h>
int c[150001] = {0};
int main() {
    int i, n, m = -1, t;
    scanf("%d", &n);
    for(i = 0; i < n; ++i) {
        scanf("%d", &t);
        if(t > m) m = t;
        ++c[t];
    }
    for(i = 0; i <= m; ++i)
        if(c[i])
            printf("%d %d\n", i, c[i]);
    return 0;
}

G:密码翻译
此道题只要完全按照题目的意思做,就能做出来,不明白为啥现场比赛时过的人那么少,,

#include <iostream>
using namespace std;
char mp[][10] = {
    {},
    {},
    {0, 'A', 'B', 'C'},
    {0, 'D', 'E', 'F'},
    {0, 'G', 'H', 'I'},
    {0, 'J', 'K', 'L'},
    {0, 'M', 'N', 'O'},
    {0, 'P', 'Q', 'R', 'S'},
    {0, 'T', 'U', 'V'},
    {0, 'W', 'X', 'Y', 'Z'}
};
char t[256];
int main() {
    char *tmp = "QWERTYUIOPASDFGHJKLZXCVBNM";
    char buf[2000];
    char ans[2000];
    char res[2000];
    int m;
    for(int i = 0; i < 26; ++i) t[tmp[i]] = i + 'A';
    while(scanf("%s", buf) != EOF) {
        memset(res, 0, sizeof(res));
        int j = 0;
        for(int i = 0; buf[i]; i += 2, ++j) {
            ans[j] = t[mp[buf[i]-'0'][buf[i+1]-'0']];
        }
        m = j / 2;
        if(j & 1) ++m;
        int x, k, y;
        for(x = 0, y = m, k = 0; x < m && y < j; ++x, ++y, k += 2) {
            res[k] = ans[x];
            res[k+1] = ans[y];
        }
        if(x < m) res[k++] = ans[m-1];
        for(int i = j - 1; i >= 0; --i) putchar(res[i]);
        puts("");
    }
    return 0;
}

H:分数线划定
此题要是懂的用sort的话,将是一道非常简单的题目。。。
注意如果最低分数存在相同的人,那么这些人全部都要入选

#include <iostream>
#include <algorithm>
using namespace std;
struct A {
    int k, s;
}a[5001];
bool cmp(A a, A b) {
    return a.s > b.s || a.s == b.s && a.k < b.k;
}
int main() {
    int n, m, i;
    scanf("%d%d", &n, &m);
    m *= 1.5;
    for(i = 0; i < n; ++i) scanf("%d%d", &a[i].k, &a[i].s);
    sort(a, a+n, cmp);
    i = m-1;
    while(i < n && a[i].s == a[m-1].s) ++i;
    printf("%d %d\n", a[m-1].s, i);
    for(n = 0; n < i; ++n) printf("%d %d\n", a[n].k, a[n].s);
    return 0;
}

I题就是A题的数据减弱版,就是为了让人水水更健康,用什么暴力的方法都能过。。

Categories: ACM Tags: , ,

HDU Farm Irrigation 1198

April 9th, 2010 PhoenixWright No comments

简单深搜,注意如何判断两个方块是否相通

#include <iostream>
using namespace std;
#define TOLEFT(T) ((T) == 'A' || (T) == 'C' || (T) == 'F' || (T) == 'G' || (T) == 'H' || (T) == 'I' || (T) == 'K')
#define TORIGHT(T) ((T) == 'B' || (T) == 'D' || (T) == 'F' || (T) == 'G' || (T) == 'I' || (T) == 'J' || (T) == 'K')
#define TOUP(T) ((T) == 'A' || (T) == 'B' || (T) == 'E' || (T) == 'G' || (T) == 'H' || (T) == 'J' || (T) == 'K')
#define TODOWN(T) ((T) == 'C' || (T) == 'D' || (T) == 'E' || (T) == 'H' || (T) == 'I' || (T) == 'J' || (T) == 'K')
int h, w;
char mp[60][60];
int v[60][60];
const int dh[] = {-1, 0, 1, 0};
const int dw[] = {0, 1, 0, -1};
const int ZONG = 0;
const int HENG = 1;
bool isConnect(int h1, int w1, int h2, int w2, int dir) {
    if(dir == HENG) {
        if(w1 > w2) {
            swap(h1, h2);
            swap(w1, w2);
        }
        return TORIGHT(mp[h1][w1]) && TOLEFT(mp[h2][w2]);
    } else {
        if(h1 > h2) {
            swap(h1, h2);
            swap(w1, w2);
        }
        return TODOWN(mp[h1][w1]) && TOUP(mp[h2][w2]);
    }
}
void dfs(int i, int j) {
    v[i][j] = 1;
    int th, tw;
    for(int k = 0; k < 4; ++k) {
        th = i + dh[k];
        tw = j + dw[k];
        if(th < 0 || th >= h || tw < 0 || tw >= w || v[th][tw] == 1 || !isConnect(i, j, th, tw, ((k&1)?HENG:ZONG))) continue;
        dfs(th, tw);
    }
}
int main() {
    int ans;
    while(scanf("%d%d", &h, &w)) {
        if(h < 0 || w < 0) break;
        ans = 0;
        for(int i = 0; i < h; ++i) scanf("%s", mp[i]);
        memset(v, 0, sizeof(v));
        for(int i = 0; i < h; ++i)
            for(int j = 0; j < w; ++j) if(v[i][j] == 0) {
                ++ans;
                dfs(i, j);
            }
        printf("%d\n", ans);
    }
}
Categories: ACM Tags: , ,

HDU 连连看 1175

April 6th, 2010 PhoenixWright No comments

题目地址
注意该题不能从外面绕
判断两点是否可以消去
消去可能是可以直接一条横线连,也可能是一个折的线或者是二个折的线
我用的方法,可以同时全部考虑进去
假设A,B是两个要判断是否消去的点,如果两点数字不同,或者都为0,当然不能消去
否则计算出A,B两点向四个方向能延伸到多远

1 2 3 4
0 0 0 0
4 3 2 1
为例
如果A=(1,1),B=(3,4)(坐标从1开始)
那么A按上右下左的顺序能延伸的范围是1, 1, 2, 1
B按上右下左的顺序能延伸的范围是2, 4, 3, 4
即向上下的话,是记录其纵坐标的范围,左右的话是记录横坐标的范围
现在先来判断上下是否连接,
那么先得出A,B两者左右延伸的公共范围,易知A左右延伸为(1,1),B为(4,4)故其没有公共范围
再来判断左右是否连接
那么先得出A,B两者上下延伸的公共范围,易知A上下延伸为(1,2),B为(2,3)故其公共范围为(2,2)
遍历纵坐标为(2,2)中的所有数且横坐标为A的横坐标的点,看每个点左右延伸范围是否有包括B点,如果有则可以消去
注意到两点刚好邻接的情况,应该将延伸范围左右扩大1
例如
1 1
这个明显可以消去,但如果按延伸范围是否包括的话,则为不能消去,所以必须加1,注意到代码注释的地方

#include <iostream>
using namespace std;
int a[1001][1001], h, w;
const int dh[] = {-1, 0, 1, 0};
const int dw[] = {0, 1, 0, -1};
struct Point {
    int h, w;
    int dir[4];
} st, en;
void cal(Point v, int *p) {
    Point tmp;
    for(int i = 0; i < 4; ++i) {
        tmp = v;
        do {
            tmp.h += dh[i];
            tmp.w += dw[i];
        } while(tmp.h >= 0 && tmp.h < h && tmp.w >= 0 && tmp.w < w && a[tmp.h][tmp.w] == 0);
        tmp.h -= dh[i];
        tmp.w -= dw[i];
        p[i] = ((i&1) ? tmp.w : tmp.h);
    }
}
void cal1(Point v, int *p, int i) {
    Point tmp;
    tmp = v;
    do {
        tmp.h += dh[i];
        tmp.w += dw[i];
    } while(tmp.h >= 0 && tmp.h < h && tmp.w >= 0 && tmp.w < w && a[tmp.h][tmp.w] == 0);
    tmp.h -= dh[i];
    tmp.w -= dw[i];
    p[i] = ((i&1) ? tmp.w : tmp.h);
}
bool solve() {
    if(a[st.h][st.w] == 0 || a[st.h][st.w] != a[en.h][en.w]) return false;
    int mmin, mmax;
    cal(st, st.dir);
    cal(en, en.dir);
    Point a1 = st, a2 = en;
    if(a1.w > a2.w) swap(a1, a2);
    mmin = max(a1.dir[0], a2.dir[0]);
    mmax = min(a1.dir[2], a2.dir[2]);
    Point tmp;
    tmp.w = a1.w;
    for(int i = mmin; i <= mmax; ++i) {
        tmp.h = i;
        cal1(tmp, tmp.dir, 1);
        //这里必须加1,理由看开头注释
        if(tmp.dir[1] + 1 >= a2.w) return true;
    }
    if(a1.h > a2.h) swap(a1, a2);
    mmin = max(a1.dir[3], a2.dir[3]);
    mmax = min(a1.dir[1], a2.dir[1]);
    tmp.h = a1.h;
    for(int i = mmin; i <= mmax; ++i) {
        tmp.w = i;
        cal1(tmp, tmp.dir, 2);
        //这里必须加1,理由看开头注释
        if(tmp.dir[2] + 1 >= a2.h) return true;
    }
    return false;
}
int main() {
    int p;
    while(scanf("%d%d", &h, &w)) {
        if(h == 0 && w == 0) break;
        for(int i = 0; i < h; ++i)
            for(int j = 0; j < w; ++j)
                scanf("%d", &a[i][j]);
        scanf("%d", &p);
        while(p--) {
            scanf("%d%d%d%d", &st.h, &st.w, &en.h, &en.w);
            st.h -= 1, st.w -= 1, en.h -= 1, en.w -= 1;
            solve() ? puts("YES") : puts("NO");
        }
    }
    return 0;
}
Categories: ACM Tags: ,

Some Funny Picture

April 6th, 2010 PhoenixWright 1 comment
Categories: Some Feel Tags:

电脑也爱干净

March 10th, 2010 PhoenixWright 3 comments

前几天电脑开机突然开不了,一开机硬盘转啊转的,可是就是没有其他任何反应,bios根本就连自检都没有,试了网上各种所谓的拔内存CPU啊之类的,仍旧不行,怀疑是电源问题,又特地从其他电脑上摘了个过来试了下还是不行,难道真如网上说的主板坏掉了么。。本来已经不报什么希望了,准备拿着主板到商场去被大宰一下,把主板拆下来之后,发觉南桥那边灰尘一大堆,就我那少的可怜的硬件知识,我是认为这灰尘一般不至于影响到什么的,但是既然都拆下来了,就蛮清理一下,于是用牙刷把那些灰尘都刷掉,抱着最后再试一次的心理接下了电源,按下开机键—————嘀—————-这。。。这。。。主板竟然就这样神奇的又恢复正常了。。激动之情难以言喻。。。看来以后要多注意电脑内部清洁了。。。

Categories: My Life Tags:

体验微软最新浏览器Pivot

December 16th, 2009 PhoenixWright 6 comments

今天小试了下微软最新的浏览器Pivot,这款由windows live labs出品的浏览器真的是好强悍,首先看看官方公布的系统要求
Recommended System Configuration: Windows 7 with Aero enabled, 2-GHz 32-bit (x86) processor, 2 gigabytes of random access memory.
Supported System Configuration: Windows Vista with Aero enabled, 2-GHz 32-bit (x86) or 64-bit (x64) processor.
Pivot is supported only on US English-based operating systems with US English date and time formats.

真是苛刻的要求,连系统都要求是英文的,不幸的是我正好完全符合上面的建议配置=-=,于是立马就下下来试用,后面发现需要邀请码,在询求无果后,下载了Pivot的破解版,可以不用邀请码就可以试用了,下载地址点这里:Pivot破解版

安装过程挺久的,期间系统还有些卡。

一打开Pivot,<是用图片形式显示的历史最常浏览的页面,点击浏览器前进后退中间的按钮,会用图形显示出你当前浏览过的所有页面,点击左下角的Pivot Collection按钮,会出现Pivot Collection Gallery页面,随便点了个狗狗的图片进去,一下子出现了一大堆狗狗的图片,而且这些图片可以随意放大随意拖动,并支持按不同的条件过滤图片,全部都以一种动态的流水型完美的表现出来,这种是没办法用文字描述的,或许只有图片才能更好的阐释。

接下来点击左下角的history按钮查看历史记录,可以看到Pivot左侧的site列表将所有浏览过的网站地址罗列出来,并支持按字母序或浏览次数排序,还有关键字过滤列表,可以根据你输入的关键字查找历史记录,以及根据访问时间查看等。而右侧又是把历史浏览网站用图片的形式显示出来,同Gallery里面一样,支持任意拖动放大,效果真是相当的炫~0~

目前就体验了这两个功能,估计还有许多强大的功能有待发掘,建议符合条件可以安装的都去装试试看,做为日常使用或许现在还不行,但至少试试看尝尝鲜还是不错的,体验一下微软的前沿技术

点击图片可以放大显示
homepage
gallery
gallery1
gallery2
gallery3
history1
history2

Categories: Technology Tags:

这会是程序员的出路么..

December 16th, 2009 PhoenixWright 2 comments

the way of coder

Categories: Some Feel Tags: ,

不用插件制作wordpress中的guestbook

November 25th, 2009 PhoenixWright 6 comments

首先,我想实现的guestbook效果是,后面留言的人显示在最前面,同时显示这个人是第几个留言的,在wordpress2.7以上虽然可以通过直接在控制面板里设定留言倒序显示,但是这样设定之后,包括普通文章的留言也会变成倒序显示,这样不是我想要的,普通文章里,应该让最早留言的人排在最前面,以显示出沙发的重要性呵呵..

当然这个是可以用插件实现的,但想想这个功能应该不会很麻烦,所以能不用插件就不用插件吧.

先说明下,这里面的更改都是基于2.7以上版本的代码,如果你的主题是仍旧使用老版本的代码,则不适用.

首先先看看你所用主题一开始是不是就默认页面是可以留言的,如果可以,则我们直接copy page.php的内容来修改,否则copy single.php的内容来修改,在你所用的主题文件夹重新建个文件GuestBook.php,将copy来的内容复制进去,然后在这文件的开头写上

<?php 
/*
Template Name: GuestBook
*/
?>

代表它是一个名字为GuestBook的模版文件,然后在该文件中查找comments_template的函数,将它里面的参数改成如下所示

comments_template('/GuestBookComments.php', true);

注意到,正常情况下,该GuestBook不同于普通文章,它不需要显示作者与发布时间,所以如果你一开始copy的有显示这些信息,建议删掉.
接下来,建立一个GuestBookComments.php的文件,将主题里原始的comments.php的所有内容copy进去,这里面的内容很多,我们不需要管这些,直接查找function_exists(’wp_list_comments’)这个字样,这里的旁边就是我们所要修改的地方,在这的下面,应该会存在wp_list_comments(’type=comment&callback=custom_comments’);类似这种的字样(你的callback可能跟我不一样),直接把这句替换成以下几条语句

global $countReply;
$countReply=get_comments_number();
wp_list_comments('type=comment&callback=guestbook_comments&reverse_top_level=TRUE');
unset($countReply);

至此,GuestBookComments.php修改完成.接下来要修改functions.php的内容,注意到我刚才说到的,wp_list_comments(’type=comment&callback=custom_comments’),看你原来主题文件里的comments.php该语句的callback是什么,记下来,如我这里是custom_comments,然后在functions.php里查找function custom_comments,将找到的整个函数复制,粘贴在该函数的下方,并把函数名改成guestbook_comments,接下来我们所要做的就是为每个留言者添加序号了.首先在该函数的开头写上

global $countReply;

然后找个合适的位置,就是你要把序号添加在每个留言的哪个地方,这个依个人所好,放在时间或者作者的后面即可,添加内容如下

<?php echo ($countReply--); ?>

至此,所有文件都已经修改完毕,接下来,只要建页面的时候,模版选择GuestBook即可!
PS:最好把允许trackbacks跟pingbacks的选项去掉,留言薄没必要有这功能~0~

Categories: Technology Tags: ,