MMX指令学习: Alpha Blend

3 评论119次阅读2009.04.07 13:47 作者:Felicia 编辑

[阅读更多]

Andre Lamothe在他的大作《3D游戏编程大师技巧》一书中提到,对游戏程序员来说,速度就是生命。因此使用优化技术来提高程序执行速度必不可少。其中SIMD编程占据很重要的位置。首先从MMX学起。以下转载自网上,留作学习之用。

MMX版本的Alpha Blend算法实现

这次我们的目标是: 超越普通的CPU玩家,用CPU的母语来优化程序!
MMX技术到现在来说可以算是基本大众化了,目前大多数个人电脑都应该能支持它。P55C, K6, PII, PIII…按照惯例,Intel公司将在以后的x86版本永远支持它。
(全文 …)

标签, | 日志分类:Game, 精华, 编译原理

[计算几何] pku1444 长方体旋转

3 评论21次阅读2008.01.23 21:07 作者:Felicia 编辑

[阅读更多]

题目意思很简单,已知长方体表面上两个点,要求这两个点的最短表面距离。
一开始我是手推展开方式的,后来发现一共有12种展开情况,手写坐标变换相当麻烦。
然后改用递归方式展开。具体方式是先把第一个点转到底面(xOy平面),然后对四个方向把底面翻开,把翻到的面作为新的底面。递归做下去,一直到第二个点也翻到底面上。
下面是我的代码:

下载: pku1444.cpp
/***********************************************************************
Author: WHU_GCC
Created Time: 2008-1-23 19:34:33
File Name: 1444.cpp
Description:
***********************************************************************/

#include <iostream>
using namespace std;
 
#define out(x) (cout << #x << ": " << x << endl)
typedef long long int64;
const int maxint = 0x7fffffff;
const int64 maxint64 = 0x7FFFFFFFFFFFFFFFLL;
template <class T> void show(T a, int n) { for (int i = 0; i < n; ++i) cout << a[i] << ' '; cout << endl; }
template <class T> void show(T a, int r, int l) { for (int i = 0; i < r; ++i) show(a[i], l); cout << endl; }
 
int ans;
 
void walk(int i, int j, int x0, int y0, int x, int y, int z, int l, int w, int h)
{
    
if (z == 0)
        
ans <?= (x0 - x) * (x0 - x) + (y0 - y) * (y0 - y);
    
else
    
{
        
if (i >= 0 && i < 2)
            
walk(i + 1, j, x0, y0 - w, x, z, w - y, l, h, w);
        
if (i <= 0 && i > -2)
            
walk(i - 1, j, x0, y0 + h, x, h - z, y, l, h, w);
        
if (j >= 0 && j < 2)
            
walk(i, j + 1, x0 - l, y0, z, y, l - x, h, w, l);
        
if (j <= 0 && j > -2)
            
walk(i, j - 1, x0 + h, y0, h - z, y, x, h, w, l);
    
}
}
 
int main()
{
    
int l, w, h, x1, y1, z1, x2, y2, z2;
    
while (scanf("%d%d%d", &l, &w, &h) != EOF)
    
{
        
scanf("%d%d%d", &x1, &y1, &z1);
        
scanf("%d%d%d", &x2, &y2, &z2);
        
if (z1 != 0 && z1 != h)
        
{
            
if (y1 != 0 && y1 != w)
            
{
                
swap(x1, z1);
                
swap(x2, z2);
                
swap(l, h);
            
}
            
else
            
{
                
swap(y1, z1);
                
swap(y2, z2);
                
swap(w, h);
            
}
        
}
        
if (z1 == h)
        
{
            
z1 = 0;
            
z2 = h - z2;
        
}
        
ans = maxint;
        
walk(0, 0, x1, y1, x2, y2, z2, l, w, h);
        
printf("%d\n", ans);
    
}
    
return 0;
}
标签, | 日志分类:计算几何

[杂题] pku3269 曼哈顿距离的性质

发表评论28次阅读2008.01.20 11:22 作者:Felicia 编辑

[阅读更多]

题目意思是给出平面上n个不相邻的点,要求到这n个点的曼哈顿距离之和最小的点的个数ans2,和这个最小距离ans1。
点的坐标范围是-10000到10000(这个很重要)

考虑到曼哈顿距离的定义:|x – x0| + |y – y0|
这个定义是线性的,对称的
因此考虑分别在两个方向上解这个问题(曼哈顿距离的性质)

  1. 对于x方向,定义x曼哈顿距离是|x – x0|,因为坐标范围很小,所以可以对所有可能的坐标,做一个线性扫描,求出fx,fx[x0]表示所有点到x0这个x坐标的x曼哈顿距离之和。对y方向相应地求出fy。
  2. 有了fx和fy,我们可以用O(1)时间算出所有点到某一坐标(x0, y0)的曼哈顿距离之和ans1。现在选出fx[x0] + fy[y0]的最小值(剔除那些输入的点),这一步我是把fx和fy分别排序做的,复杂度O(nlogn)。应该能利用点的不相邻性质做得更好。
  3. 现在统计满足题意的点的个数。利用fx计算数组nx,nx[i].coor表示此点的x坐标,nx[i].num表示x坐标是nx[i].coor的点的个数,nx[i].dist表示所有点到nx[i].coor的x曼哈顿距离之和。相应地计算ny。计算数组dd,dd[i]表示所有点到第i个点的曼哈顿距离之和。令t1 = sum(nx[i].num * ny[i].num), if (nx[i].dist + ny[i].dist == ans1)。令t2 = (dd[i] == ans1) 的个数。因此ans2 = t1 – t2。

下面是我的代码:

下载: pku3269.cpp
/**********************************************************************
Author: WHU_GCC
Created Time: 2008-1-19 20:26:13
File Name: pku3269.cpp
Description:
**********************************************************************/

#include <iostream>
#include <set>
using namespace std;
 
#define out(x) (cout << #x << ": " << x << endl)
typedef long long int64;
const int maxint = 0x7FFFFFFF;
const int64 maxint64 = 0x7FFFFFFFFFFFFFFFLL;
template <class T> void show(T a, int n) { for (int i = 0; i < n; ++i) cout << a[i] << ' '; cout << endl; }
template <class T> void show(T a, int r, int l) { for (int i = 0; i < r; ++i) show(a[i], l); cout << endl; }
 
const int maxn = 10010;
 
struct node_t
{
    
int dist;
    
int coor;
};
 
struct num_t
{
    
int dist;
    
int num;
};
 
bool operator <(const node_t &a, const node_t &b)
{
    
return a.dist < b.dist;
}
 
int n;
int x[maxn];
int y[maxn];
node_t fx[maxn * 2];
node_t fy[maxn * 2];
int hx[maxn * 2];
int hy[maxn * 2];
int minx, miny, maxx, maxy;
int dd[maxn];
 
set <pair<int, int> > ptrset;
 
num_t nx[maxn];
num_t ny[maxn];
int lnx, lny;
 
int main()
{
    
scanf("%d", &n);
    
memset(hx, 0, sizeof(hx));
    
memset(hy, 0, sizeof(hy));
    
    
minx = miny = maxint;
    
maxx = maxy = 0;
    
ptrset.clear();
    
for (int i = 0; i < n; i++)
    
{
        
scanf("%d%d", &x[i], &y[i]);
        
x[i] += 10000;
        
y[i] += 10000;
        
ptrset.insert(make_pair(x[i], y[i]));
        
hx[x[i]]++;
        
hy[y[i]]++;
        
minx <?= x[i];
        
miny <?= y[i];
        
maxx >?= x[i];
        
maxy >?= y[i];
    
}
    
fx[minx].dist = 0;
    
fx[minx].coor = minx;
    
for (int i = minx + 1; i <= maxx; i++)
        
fx[minx].dist += abs(i - minx) * hx[i];
    
int numrx = n - hx[minx];
    
int nummx = hx[minx];
    
int numlx = 0;
    
for (int i = minx + 1; i <= maxx; i++)
    
{
        
fx[i].dist = fx[i - 1].dist + numlx + nummx - numrx;
        
fx[i].coor = i;
        
numlx += nummx;
        
nummx = hx[i];
        
numrx -= nummx;
    
}
    
fy[miny].dist = 0;
    
fy[miny].coor = miny;
    
for (int i = miny + 1; i <= maxy; i++)
        
fy[miny].dist += abs(i - miny) * hy[i];
    
int numry = n - hy[miny];
    
int nummy = hy[miny];
    
int numly = 0;
    
for (int i = miny + 1; i <= maxy; i++)
    
{
        
fy[i].dist = fy[i - 1].dist + numly + nummy - numry;
        
fy[i].coor = i;
        
numly += nummy;
        
nummy = hy[i];
        
numry -= nummy;
    
}
    
for (int i = 0; i < n; i++)
        
dd[i] = fx[x[i]].dist + fy[y[i]].dist;
 
    
sort(fx + minx, fx + maxx + 1);
    
sort(fy + miny, fy + maxy + 1);
    
int ans1;
    
for (int i = minx; i <= maxx; i++)
        
for (int j = miny; j <= maxy; j++)
        
{
            
if (ptrset.count(make_pair(fx[i].coor, fy[j].coor)) == 0)
            
{
                
ans1 = fx[i].dist + fy[j].dist;
                
goto end;
            
}
        
}
    
end:;
    
lnx = 0;
    
for (int i = minx; i <= maxx; i++)
    
{
        
if (i == minx)
        
{
            
nx[lnx].dist = fx[i].dist;
            
nx[lnx].num = 1;
            
lnx++;
        
}
        
else
        
{
            
if (fx[i].dist == nx[lnx - 1].dist)
                
nx[lnx - 1].num++;
            
else
            
{
                
nx[lnx].dist = fx[i].dist;
                
nx[lnx].num = 1;
                
lnx++;
            
}
        
}
    
}
 
    
lny = 0;
    
for (int i = miny; i <= maxy; i++)
    
{
        
if (i == miny)
        
{
            
ny[lny].dist = fy[i].dist;
            
ny[lny].num = 1;
            
lny++;
        
}
        
else
        
{
            
if (fy[i].dist == ny[lny - 1].dist)
                
ny[lny - 1].num++;
            
else
            
{
                
ny[lny].dist = fy[i].dist;
                
ny[lny].num = 1;
                
lny++;
            
}
        
}
    
}
    
int ans2 = 0;
    
for (int i = 0; i < lnx; i++)
        
if (nx[i].dist <= ans1)
        
{
            
for (int j = 0; j < lny; j++)
                
if (nx[i].dist + ny[j].dist <= ans1)
                
{
                    
if (nx[i].dist + ny[j].dist == ans1)
                        
ans2 += nx[i].num * ny[j].num;
                
}
                
else break;
        
}
        
else break;
    
for (int i = 0; i < n; i++)
        
if (dd[i] == ans1)
            
ans2--;
    
printf("%d %d\n", ans1, ans2);
    
return 0;
}
标签 | 日志分类:杂题

[杂题] pku3263 区间性质

1个评论16次阅读2008.01.12 22:02 作者:Felicia 编辑

[阅读更多]

这个题目本质上要解决一个问题,给出一些区间[ai, bi)和一个数组,求数组中每个元素被区间覆盖的次数。

一开始想了个做法是线段树,后来想了个O(n)的做法。具体过程如下:

  1. 去掉重复区间
  2. f数组置0
  3. 对每个区间[ai, bi),令f[ai]++,f[bi]–
  4. 设答案数组为c,则c[i] = sum(f[j]), 1 <= j <= i

关键是理解f数组的意义:f[i]表示第i个点对后续点的影响,而f[ai]++,f[bi]–保证了区间外的点不受影响,区间内的点都受+1的影响

以下是我的代码:

下载: pku3263.cpp
/**********************************************************************
Author: WHU_GCC
Created Time: 2008-1-12 21:14:15
File Name: pku3263.cpp
Description:
**********************************************************************/

#include <iostream>
using namespace std;
 
#define out(x) (cout << #x << ": " << x << endl)
typedef long long int64;
const int maxint = 0x7FFFFFFF;
const int64 maxint64 = 0x7FFFFFFFFFFFFFFFLL;
template <class T> void show(T a, int n) { for (int i = 0; i < n; ++i) cout << a[i] << ' '; cout << endl; }
template <class T> void show(T a, int r, int l) { for (int i = 0; i < r; ++i) show(a[i], l); cout << endl; }
 
const int maxr = 10010;
const int maxn = 10010;
 
struct node_t
{
    
int l, r;
};
 
bool operator ==(const node_t &a, const node_t &b)
{
    
return a.l == b.l && a.r == b.r;
}
 
bool operator <(const node_t &a, const node_t &b)
{
    
return a.l < b.l || a.l == b.l && a.r < b.r;
}
 
node_t p[maxr];
int f[maxn];
int a[maxn];
 
int n, I, H, r;
 
int main()
{
    
scanf("%d%d%d%d", &n, &I, &H, &r);
    
for (int i = 0; i < r; i++)
    
{
        
scanf("%d%d", &p[i].l, &p[i].r);
        
if (p[i].l > p[i].r)
            
swap(p[i].l, p[i].r);
    
}
    
sort(p, p + r);
    
r = unique(p, p + r) - p;
    
memset(f, 0, sizeof(f));
    
for (int i = 0; i < r; i++)
    
{
        
f[p[i].l + 1]--;
        
f[p[i].r]++;
    
}
    
a[0] = 0;
    
for (int i = 1; i <= n; i++)
        
a[i] = a[i - 1] + f[i];
    
for (int i = 1; i <= n; i++)
        
printf("%d\n", a[i] + H);
    
return 0;
}
标签 | 日志分类:杂题

[杂题] SPOJ744

发表评论30次阅读2008.01.04 20:45 作者:Felicia 编辑

[阅读更多]

这个题目是Sherlock推荐我做的。
题目意思是给一个长度为n(n <= 100000)的序列,里面的数满足1 <= a[i] <= n。要找一个最长的连续子串,使得这个子串是1..k的一个排列。

昨天晚上mmd想了个n^(3/2)的算法,今天Sherlock想了个nlogn的算法。我一直在考虑有没有O(n)的算法。我觉得应该有的。
今天晚上吃饭的时候忽然想出来了。回来写了果然AC。

下面是我的算法:
注意到满足题目要求的序列必有如下性质:

  1. 最大值等于长度
  2. 必含1
  3. 序列和为k * (k + 1) / 2
  4. 序列内元素不重复

如果一个序列同时满足性质3,4,那么一定符合题目要求。
于是如果做了O(n)的预处理,可以用O(1)的时间验证某序列是否满足题目要求。
然后我的算法就顺理成章了:

  1. 预处理s[i],为序列的部分和
  2. 预处理rl[i],为从i开始往右,最长的不重复序列的末端的下标
  3. 预处理m[i],为从i开始,到i右边的第一个1(或者最右端),这一段数的最大值。
  4. 枚举左端点lp,则lp到其右边的第一个1(或者最右端)中的最大值为m[lp]。把m[lp]作为序列长度,则序列的右端点为lp + m[lp] – 1。利用s[i]和rl[i]数组可以验证这段序列是否满足题目要求,若满足,就更新最优解。
  5. 把输入序列反过来,重复步骤1-4。

以上每步的时间复杂度都是O(n),故算法总的时间复杂度也是O(n)。

下面是我的代码

下载: spoj744.cpp
/**********************************************************************
Author: WHU_GCC
Created Time: 2008-1-4 18:06:14
File Name: spoj744.cpp
Description:
**********************************************************************/

#include <iostream>
using namespace std;
 
#define out(x) (cout << #x << ": " << x << endl)
typedef long long int64;
const int maxint = 0x7FFFFFFF;
const int64 maxint64 = 0x7FFFFFFFFFFFFFFFLL;
template <class T> void show(T a, int n) { for (int i = 0; i < n; ++i) cout << a[i] << ' '; cout << endl; }
template <class T> void show(T a, int r, int l) { for (int i = 0; i < r; ++i) show(a[i], l); cout << endl; }
 
const int maxn = 100010;
 
int n;
int a[maxn];
int s[maxn];
int rl[maxn];
int hash[maxn];
int m[maxn];
 
int calc()
{
    
s[0] = 0;
    
for (int i = 1; i <= n; i++)
        
s[i] = a[i] + s[i - 1];
 
    
memset(hash, 0, sizeof(hash));
    
for (int i = 1, j = 1; i <= n; i++)
    
{
        
while (j <= n && hash[a[j]] == 0)
            
hash[a[j++]] = 1;
        
rl[i] = j - 1;
        
hash[a[i]] = 0;
    
}
 
    
for (int i = n; i >= 1; i--)
        
if (a[i] == 1)
            
m[i] = 1;
        
else if (i == n)
            
m[i] = a[i];
        
else
            
m[i] = max(m[i + 1], a[i]);
 
    
int ret = 0;
    
for (int i = 1; i <= n; i++)
        
if (rl[i] >= i + m[i] - 1 && s[i + m[i] - 1] - s[i - 1] == m[i] * (m[i] + 1) / 2)
            
ret >?= m[i];
    
return ret;
}
 
int main()
{
    
scanf("%d", &n);
    
for (int i = 1; i <= n; i++)
        
scanf("%d", &a[i]);
    
int ans = calc();
    
reverse(a + 1, a + n + 1);
    
ans >?= calc();
    
printf("%d\n", ans);
    
return 0;
}
标签, | 日志分类:杂题