[HDOJ3545 Board Coloring]【动态规划】【四维求和】★★

【题目大意】
给定一个4*n的矩阵a[i,j]。(1<=i<=4 1<=j<=n)你要在里面填0~255的数字。
然后使得填了以后满足以下约束:
1:a[i,j]>=a[i,j-1]
2:对于一些给定了限制(i,j,i’,j’),必须满足a[i,j]=a[i’,j’]。
【算法分析】
非常非常好的题目,没有涉及到什么高深的算法,但是非常巧妙,对于锻炼思维能力有很好的效果。
令F[p[1]][p[2]][p[3]][p[4]]表示现在涂完颜色c,然后第i列涂到p[i]时的方案数。
然后很容得出F[p[1]][p[2]][p[3]][p[4]]=Sum{F[c-1][p[1]’][p[2]’][p[3]’][p[4]’]}。
其中p[i]’<=p[i]。
然后对于那些相等限制,就是假如某个状态(p[i]但是这个求和还是不太简单。
一开始我搞了3个辅助数组一起求T_T。。。
后来猛然发现本质就是在“固定”其它3个位,只留现在这个位来动。然后就想到一个一个位顺着弄。
就变成现在程序这样子。
实际上是不需要滚动数组的。因为我们每次求出的都是下一次所需要的原数组。
【其它】1Y
【Record】时间跑成第一了。可能因为其它人大多用了滚动数组吧。
【CODE】
#include #include #include const int N=16;
const int Mod=100000;
int n,m;
int F[N][N][N][N];
int v[N][N][N][N];

void init(){
     memset(v,0,sizeof(v));
     scanf("%d%d",&n,&m);
     int i,p[5],x1,y1,x2,y2;
     for (i=1;i<=m;i++){
       scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
       for (p[1]=0;p[1]<=n;p[1]++)
         for (p[2]=0;p[2]<=n;p[2]++)
           for (p[3]=0;p[3]<=n;p[3]++)
             for (p[4]=0;p[4]<=n;p[4]++)
               if ( (p[x1]                 v[p[1]][p[2]][p[3]][p[4]]=1;
     }
}

void work(){
     memset(F,0,sizeof(F));
     int p[5],c,i,delta;
     F[0][0][0][0]=1;
     for (c=1;c<=256;c++){
         for (delta=1;delta<=4;delta++)
           for (p[1]=0;p[1]<=n;p[1]++)
             for (p[2]=0;p[2]<=n;p[2]++)
               for (p[3]=0;p[3]<=n;p[3]++)
                 for (p[4]=0;p[4]<=n;p[4]++)
                   if (p[delta]){
                     int &t=F[p[1]][p[2]][p[3]][p[4]];
                     p[delta]–;
                     t    +=F[p[1]][p[2]][p[3]][p[4]];
                     p[delta]++;
                     if (t>=Mod) t-=Mod;
                   }
         for (p[1]=0;p[1]<=n;p[1]++)
           for (p[2]=0;p[2]<=n;p[2]++)
             for (p[3]=0;p[3]<=n;p[3]++)
               for (p[4]=0;p[4]<=n;p[4]++)
                 if (v[p[1]][p[2]][p[3]][p[4]])
                   F[p[1]][p[2]][p[3]][p[4]]=0;
     }
     printf("%05dn",F[n][n][n][n]);
}

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

[SGU110 Dungeon]【计算几何】【模拟】【球面反射】

【题目大意】
给定n个球,与一条射线,然后让你模拟这个射线在这些球之间弹,输出弹到得球的标号。如果弹到<=10个就有多少输出多少。否则,输出前10个+ 'etc.'

【算法分析】
我们用三维向量来解决。
1、首先根据点到球心距离等于半径 + 我们的运动向量列出一个方程,解这个一元二次方程可以得到我们什么时候碰到这个球。
2、然后碰到以后呢。就把射线起点,球上的碰触点,球心这三个点,搞成一个平面,然后再在上面以向量:碰触点->球心作为法线,考虑镜面反射即可。
然后这里我再次用了一个解方程+向量伸缩的方法得到了他反射以后的向量。

关于第二步的反射部分,我们所知道的条件有:入射向量,法线向量。我的做法具体就是先构造出向量tmp=发现向量-入射向量。并使得|tmp|=|入射向量|。然后就可以通过一些几何知识来搞了。

【其它】WA了几遍,因为我解一元二次方程的时候判断无解写得是delta<1e-6,然后如果1e-6<=delta<0的话,我就先将它置为0再进行后面的计算。
我改成delta<0就为无解就AC了。T_T。。。精度什么的,最讨厌了。

【CODE】
#include #include #include #include #include #define dis(A,B) sqrt( (A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y)+(A.z-B.z)*(A.z-B.z) )
#define Sqr(x) ((x)*(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
using namespace std;
const int N=1005;
const double eps=1e-6;
int n;
double res1,res2;
struct Circle{double x,y,z,r;}a[N];
struct Point{double x,y,z;}p,fx;

void init(){
     scanf("%d",&n);
     int i,j;
     for (i=1;i<=n;i++)
       cin >> a[i].x >> a[i].y >> a[i].z >> a[i].r;
     cin >> p.x >> p.y >> p.z >> fx.x >> fx.y >> fx.z;
     fx.x-=p.x;
     fx.y-=p.y;
     fx.z-=p.z;
}

Point Get_Point(double rate){
      Point res;
      res.x=fx.x*rate+p.x;
      res.y=fx.y*rate+p.y;
      res.z=fx.z*rate+p.z;
      return res;
}

double Get_Meet_Rate(Circle Cir){
       double a,b,c,delta,x1,x2;
       Point tmp;
       tmp.x=Cir.x-p.x;
       tmp.y=Cir.y-p.y;
       tmp.z=Cir.z-p.z;
       a=Sqr(fx.x)+Sqr(fx.y)+Sqr(fx.z);
       b=-2*(fx.x*tmp.x + fx.y*tmp.y + fx.z*tmp.z);
       c=-Sqr(Cir.r)+Sqr(tmp.x)+Sqr(tmp.y)+Sqr(tmp.z);
       delta=b*b-4*a*c;
       if (delta<0) return 1e22;
       delta=sqrt(delta);
       x1=(-b+delta)/(2*a);
       x2=(-b-delta)/(2*a);
       if (x1<=eps) x1=1e22;
       if (x2<=eps) x2=1e22;
       res1=x1; res2=x2;
       return min(x1,x2);
}

Point opposite(Point tmp){
      Point res;
      res.x=-tmp.x;
      res.y=-tmp.y;
      res.z=-tmp.z;
      return res;
}

void Reflect(int i,double rate){
     Point cross=Get_Point(rate);
     Get_Meet_Rate(a[i]);
     if ( fabs(res1-res2) < eps ){
       p=cross;
       return;
     }
     Point normal_line,tmp;
     normal_line.x=(a[i].x-cross.x)/2;
     normal_line.y=(a[i].y-cross.y)/2;
     normal_line.z=(a[i].z-cross.z)/2;
     double t=Sqr(normal_line.x)+Sqr(normal_line.y)+Sqr(normal_line.z);
     t/=(normal_line.x*fx.x + normal_line.y*fx.y + normal_line.z*fx.z);
     fx.x*=t; fx.y*=t; fx.z*=t;
     normal_line.x*=2;
     normal_line.y*=2;
     normal_line.z*=2;
    
     tmp.x=normal_line.x-fx.x;
     tmp.y=normal_line.y-fx.y;
     tmp.z=normal_line.z-fx.z;
    
     fx=opposite(tmp);
     p=cross;
    
     bool flag=false;
     if (fx.x>10) flag=true;
     if (fx.y>10) flag=true;
     if (fx.z>10) flag=true;
     if (!flag){
       fx.x*=10;
       fx.y*=10;
       fx.z*=10;
     }
}

void work(){
     int i,best,cnt=0;
     double rate,tmp;
     while (1){
           rate=1e20;
           best=0;
           for (i=1;i<=n;i++){
               tmp=Get_Meet_Rate(a[i]);
               if (tmp                 rate=tmp;
                 best=i;
               }
           }
           if (best==0) break;
           Reflect(best,rate);
           cnt++;
           if (cnt>10) break;
           if (cnt!=1) cout << " ";
           cout << best;
     }
     if (cnt>10) cout << " etc." << endl;
            else cout << endl;
}

int main(){
    init();
    work();
    return 0;
}

[HDOJ2966 In case of failure]【平面每个点的最近点对】【暴力T_T】

【题目大意】
给定n个点,求每个点到离他最近点的距离。
【算法分析】
V图什么的,最讨厌了。。。
我直接就暴力了。。。
按x把点排序,然后按j递增序,枚举i-j的点和i+j的点。如果(a[i].x-a[i-j].x)^2 > dis && (a[i].x-a[i+j].x)^2 > dis
那么就break吧。
复杂度还是n^2的。
其中dis为之前枚举所得的较优解。
【其它】果断排到超级后面。
叉姐说,见到题目要先水一水,水不过再切。。。= =
【CODE】
#include #include #include #include #define dis(A,B) ( (lld)(A.x-B.x)*(lld)(A.x-B.x) + (lld)(A.y-B.y)*(lld)(A.y-B.y) )
#define Sqr(x) ((lld)(x)*(lld)(x))
using namespace std;
typedef long long lld;
const lld INF=(lld)(2000000000)*(lld)(2000000000);
int Tc;
int n;
struct Point{int x,y,pos; lld dis;}a[105555];

bool cmp(Point A,Point B){
return A.x}

void init(){
scanf("%d",&n);
int i;
for (i=1;i<=n;i++){
scanf("%d%d",&a[i].x,&a[i].y);
a[i].pos=i;
}
sort(a+1,a+n+1,cmp);
}

void work(){
int i,j;
bool flag;
lld tmp;
for (i=1;i<=n;i++){
a[i].dis=INF;
for (j=1;(i-j>=1 || i+j<=n);j++){
flag=false;
if (i-j>=1 && Sqr(a[i].x-a[i-j].x) tmp=dis(a[i],a[i-j]);
if (tmp flag=true;
}
if (i+j<=n && Sqr(a[i+j].x-a[i].x) tmp=dis(a[i],a[i+j]);
if (tmp flag=true;
}
if (!flag) break;
}
}
}

bool cmp2(Point A,Point B){
return A.pos}

void output(){
sort(a+1,a+n+1,cmp2);
for (int i=1;i<=n;i++)
printf("%I64dn",a[i].dis);
}

int main(){
scanf("%d",&Tc);
for (int i=0;i init();
work();
output();
}
}

[SGU108 Self-numbers 2]【枚举】【常数题】

【算法分析】

就是类似筛法那样弄下去。但是特殊的是i只会影响到一个>i的数。

而且之多+6*9。所以,我们可以开滚动数组来弄。(开10^7 bool会MLE)

然后模拟高精度那样+1。就可以很方便地维护该数所指的下一个数next。

【其它】速度貌似不错。180+MS。

【Record】

1057066 13.08.10 12:37 edward 108 .CPP Accepted 186 ms 43 kb

【CODE】

#include

[SGU106 The Equation]【欧几里得拓展算法解二元一次方程】

【题目大意】

给定ax+by+c=0。求x1<=x<=x2 && y1<=y<=y2的解有多少组。

【算法分析】

见到缺了这种例题,又没A这题,那么就做一下。

例题系列。

首先判断掉a和b中存在0的情况。然后exgcd解出ax+by=gcd(a,b)的一个初始解,

然后假如-c%gcd(a,b)!=0。那么无解。否则a/=g b/=g c/=g。然后x就只能+-b,y对应地+-a。然后用个除法搞一下就可以了。

一开始我后面取范围用倍增算法,就是2进制步长那种跨越的。第3个点居然就TLE了= =。

【时间复杂度】O(lg (x+y))

【空间复杂度】O(1)

【CODE】

#include