[UASCO 6.1.2 Serious challenges]动态规划

【算法分析】

这题我做过类似的,基本思想是做垂线,然后向左右延伸。

算法步骤的思想如下:

枚举每一个点,然后尽量向上延伸,直到碰到墙壁或者坏了的点,然后将垂直的线段向左右尽量延伸,最后结果就在这M*N个极大子矩形当中。

这个算法很容易证明:如果是答案的子矩阵,那么它一定上下左右都被某个点(或边界)卡住,否则他还可以延伸就不是最大的了。然后这个算法把每一个格子当做是最大矩形的上顶点(即阻止它向上延伸的顶点),然后向下延伸任意长度的各种情况都枚举了,那么向左右延伸,就必然可以包含最优解。

最后利用DP的思想减少重复,复杂度达到O(RC),使用滚动数组空间复杂度达到O(C)

【其它】

这题搞死我了,一直WA at 10,然后用pascal写了一遍,又是WA at 10

于是搞来搞去,最后发现原来是一个地方没有初始化,一交,终于AC,ORZ!!!

USACO终于圆满了,在此纪念一下

Chapter 1 DONE 2009.08.29 Getting started Chapter 2 DONE 2007.08.09 Bigger Challenges Chapter 3 DONE 2008.07.22 Techniques more subtle Chapter 4 DONE 2008.08.05 Advanced algorithms and difficult drills Chapter 5 DONE 2010.02.19 Serious challenges Chapter 6 DONE 2010.02.20 Contest Practice

这时间差好恐怖。。。

【CODE】

/*
ID:jie22221
TASK:rectbarn
LANG:C++
*/
#include #include #define min(x,y) (x)<(y)?(x):(y)
#define max(x,y) (x)>(y)?(x):(y)
const int INF=0x7FFFFFFF/5;
struct zb{int x,y;}s[31111];
int m,n,p,ans=0;
int u[2][3111],l[2][3111],r[2][3111],ll[3111],rr[3111];
bool v[2][3111];

void sort(int L,int R){
    if (L>=R) return;
    zb mid=s[(L+R)/2],tmp;
    int i=L,j=R;
    while (i        while (s[i].x        while (s[j].x>mid.x || s[j].x==mid.x && s[j].y>mid.y) j–;
        if (i<=j){
            tmp=s[i]; s[i]=s[j]; s[j]=tmp;
            i++; j–;
        }
    }
    sort(L,j);
    sort(i,R);
}

void init(){
    memset(s,0,sizeof(s));
    scanf("%d%d%dn",&m,&n,&p);
    for (int i=0;i      scanf("%d%dn",&s[i].x,&s[i].y);
    sort(0,p-1);
    for (int i=0;i<=n+1;i++){
        v[0][i]=true;
        l[0][i]=INF;
        r[0][i]=INF;
    }
}

void work(){
    int si=0,i,j,R,pi,tmp;
    for (R=1;R<=m;R++){
        i=R&1; pi=i^1;
        memset(v[i],false,sizeof(v[i]));
        v[i][0]=true; v[i][n+1]=true;
        for (j=1;j<=n;j++)
          while (si

              si++;
              v[i][j]=true;
          }
//      deal ll
        for (j=1;j<=n;j++)
          if (!v[i][j])
            if (v[i][j-1]) ll[j]=0;
                      else ll[j]=ll[j-1]+1;
//      deal rr
        for (j=n;j>=1;j–)
          if (!v[i][j])
            if (v[i][j+1]) rr[j]=0;
                      else rr[j]=rr[j+1]+1;
//      dp
        for (j=1;j<=n;j++)
          if (!v[i][j]){
              if (v[pi][j]){
                  u[i][j]=0;
                  l[i][j]=ll[j];
                  r[i][j]=rr[j];
              }
              else{
                  u[i][j]=u[pi][j]+1;
                  l[i][j]=min(ll[j],l[pi][j]);
                  r[i][j]=min(rr[j],r[pi][j]);
              }
              tmp=(l[i][j]+r[i][j]+1)*(u[i][j]+1);
              if (tmp>ans)
                ans=tmp;
          }
    }
}

int main(){
    freopen("rectbarn.in","r",stdin);
    freopen("rectbarn.out","w",stdout);
    init();
    work();
    printf("%dn",ans);
}

[USACO 6.1.1 Postal Vans]高精度、找规律

【题目大意】找一个特殊图哈密顿回路的方案数。

【算法分析】

我先DFS了一下,感觉如果列是4的话,应该是一个4阶的递归式。

然后找到规律。。。OH,YEAH,AC

规律就是F[n]=2F[n-1]+2F[n-2]-2F[n-3]+F[n-4]

【其它】1A

【CODE】

/*
ID:jie22221
TASK:vans
LANG:C++
*/
#include #include const int M=100;
const int Mod=100000;
struct bigint{int d[M+1];};
int px[4]={-1,1,0,0},py[4]={0,0,1,-1};
int n,limit,total,ans;
bigint f[1001];
bool v[5][5];

void dfs(int x,int y){
    if (total==4*limit-1){
        if (x+y==3) ans++;
        return;
    }
    int k,tx,ty;
    v[x][y]=true;
    total++;
    for (k=0;k<4;k++){
        tx=x+px[k]; ty=y+py[k];
        if (tx<1 || tx>4 || ty<1 || ty>limit || v[tx][ty]) continue;
        dfs(tx,ty);
    }   
    v[x][y]=false;
    total–;
}   

bigint plus(bigint x,bigint y){
    for (int i=1;i<=M;i++) x.d[i]+=y.d[i];
    for (int i=M;i>=1;i–){
        x.d[i-1]+=x.d[i]/Mod;
        x.d[i]%=Mod;
    }   
    return x;
}

bigint dec(bigint x,bigint y){
    for (int i=1;i<=M;i++) x.d[i]-=y.d[i];
    for (int i=M;i>=1;i–)
      while (x.d[i]<0){
          x.d[i]+=Mod;
          x.d[i-1]–;
      }   
    return x;
}   

void init(){
    memset(f,0,sizeof(f));
    for (limit=1;limit<=4;limit++){
        ans=0;
        total=0;
        dfs(1,1);
        f[limit].d[M]=ans;
    }
}

void put(bigint x){
    int st;
    for (st=0;st<=M;st++)
      if (x.d[st]) break;
    printf("%d",x.d[st]);
    for (int i=st+1;i<=M;i++){
        int div=Mod/10,mod=Mod;
        while (div>0){
            printf("%d",x.d[i]%mod/div);
            mod/=10;
            div/=10;
        }   
    }   
    printf("n");
}   

void work(){
    scanf("%d",&n);
    if (n<=4){
        printf("%dn",f[n].d[M]);
        return;
    }
    bigint tmp;
    for (int i=5;i<=n;i++){
       memset(tmp.d,0,sizeof(tmp.d));
       tmp=plus(f[i-1],f[i-1]);
       f[i]=plus(f[i],tmp);
       tmp=plus(f[i-2],f[i-2]);
       f[i]=plus(f[i],tmp);
       tmp=plus(f[i-3],f[i-3]);
       f[i]=dec(f[i],tmp);
       f[i]=plus(f[i],f[i-4]);
    }
    put(f[n]);
}   

int main(){
    freopen("vans.in","r",stdin);
    freopen("vans.out","w",stdout);
    init();
    work();
}   

[USACO 5.3 Window Area]矩形切割

【题目翻译】http://www.wzoi.org/usaco/15%5C305.asp

【算法分析】

矩形切割,直接模拟就可以。

【其它】囧,以前居然卡在这题水题上。1A

USER: wei jie chen [jie22221]TASK: windowLANG: C++Compiling…Compile: OKExecuting… Test 1: TEST OK [0.000 secs, 2804 KB] Test 2: TEST OK [0.000 secs, 2804 KB] Test 3: TEST OK [0.000 secs, 2804 KB] Test 4: TEST OK [0.011 secs, 2804 KB] Test 5: TEST OK [0.011 secs, 2804 KB] Test 6: TEST OK [0.011 secs, 2804 KB] Test 7: TEST OK [0.022 secs, 2804 KB] Test 8: TEST OK [0.022 secs, 2804 KB] Test 9: TEST OK [0.000 secs, 2804 KB] Test 10: TEST OK [0.000 secs, 2804 KB] Test 11: TEST OK [0.022 secs, 2804 KB]All tests OK.

YOUR PROGRAM (‘window’) WORKED FIRST TIME! That’s fantastic– and a rare thing. Please accept these special automatedcongratulations.

【CODE】

/*
ID:jie22221
TASK:window
LANG:C++
*/
#include #include #include #include #include using namespace std;
const int N=100;
struct zfx{int x1,y1,x2,y2;char mark;}d[N];
int n;
double ans;inline int area(int x1,int y1,int x2,int y2){
    return (x2-x1)*(y2-y1);
}    void cut(int i,int x1,int y1,int x2,int y2){
    if (x1>=x2 || y1>=y2) return;
    if (i>n){
        ans+=area(x1,y1,x2,y2);
        return;
    }   
    if (x1    if (x2>d[i].x2) cut(i+1,max(x1,d[i].x2),y1,x2,y2);
    if (y1    if (y2>d[i].y2) cut(i+1,max(x1,d[i].x1),max(d[i].y2,y1),min(x2,d[i].x2),y2);
}   int main(){
    freopen("window.in","r",stdin);
    freopen("window.out","w",stdout);
    char op;
    int pos;
    zfx tmp;
    for (;;){
        op=’ ‘;
        while (scanf("%c",&op)!=EOF && (op==’ ‘ || op==’n’));
        if (op==’ ‘ || op==’n’) break;
        switch (op){
            case ‘w’:
                n++;
                scanf("(%c,%d,%d,%d,%d)",&d[n].mark,&d[n].x1,&d[n].y1,&d[n].x2,&d[n].y2);
                if (d[n].x1>d[n].x2) swap(d[n].x1,d[n].x2);
                if (d[n].y1>d[n].y2) swap(d[n].y1,d[n].y2);
                break;
            case ‘t’:
                scanf("(%c)",&op);
                zfx tmp;
                for (pos=1;pos<=n;pos++)
                  if (d[pos].mark==op) break;
                tmp=d[pos];
                for (int i=pos;i                d[n]=tmp;
                break;
            case ‘b’:
                scanf("(%c)",&op);
                for (pos=1;pos<=n;pos++)
                  if (d[pos].mark==op) break;
                tmp=d[pos];
                for (int i=pos;i>1;i–) d[i]=d[i-1];
                d[1]=tmp;
                break;
            case ‘d’:
                scanf("(%c)",&op);
                for (pos=1;pos<=n;pos++)
                  if (d[pos].mark==op) break;
                for (int i=pos;i                n–;
                break;
            case ‘s’:
                scanf("(%c)",&op);
                for (pos=1;pos<=n;pos++)
                  if (d[pos].mark==op) break;
                ans=0;
                cut(pos+1,d[pos].x1,d[pos].y1,d[pos].x2,d[pos].y2);
                double S=area(d[pos].x1,d[pos].y1,d[pos].x2,d[pos].y2);
                printf("%.3lfn",ans/S*100);
                break;
        }   
    }   
}   

[NOI2007 day2 项链工厂]线段树、维护相对位置**

【题目】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1493

【算法分析】

这题一开始被那个翻转给吓到了,但是画了个图以后发现只是将顺时针变成逆时针,位置全变成n+2-i而已。假如我们都当成是未翻转前去看的话,就很容易想到维护方法。

定义一个偏移量pyl(拼音= =),表示向右旋转了多少格。在搞一个bool型表示是否翻转。

然后翻转前的R操作,直接偏移量+x

如果是翻转后的话,那么就是-x,因为数字变逆时针了,你要全部当成翻转前的情况的话,就相当于逆时针转动。(画了图就比较好理解)

然后有了这些性质,就维护一棵线段树即可。

另外注意数组里的color和线段树里的同步问题。

总之就是一些细节的东西。

【其它】1A,居然垫底了

Rank Run ID User Memory Time Language Code Length Submit Time 1 5318 root 18164K 2839MS Pascal 5.70K 2009-07-08 11:37:16 2 8949 oimaster 29172K 3514MS Pascal 5.26K 2010-01-30 11:57:51 3 5545 pyf 24144K 3948MS Pascal 5.17K 2009-07-10 20:11:20 4 9596 sadness 26860K 4107MS G++ 6.31K 2010-02-18 20:57:45 5 5942 tracyhenry 37808K 4342MS Pascal 4.89K 2009-08-04 15:24:39 6 9366 lilymona 41140K 4592MS G++ 2.69K 2010-02-11 02:14:30 7 5547 xlmj531 17064K 4608MS Pascal 5.92K 2009-07-10 20:14:23 8 5544 tclsm1991 23176K 4686MS Pascal 7.96K 2009-07-10 20:09:27 9 5542 tclsm 23168K 4840MS Pascal 7.96K 2009-07-10 20:08:04 10 5828 wu3412790 44288K 4871MS Pascal 3.55K 2009-07-18 22:11:15 11 9641 edward_mj 30800K 4873MS G++ 4.13K 2010-02-19 21:25:25

【CODE】

#include

[NOI2009 day2 管道取珠]动态规划、巧妙转化**

【题目】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1566

【算法分析】

这个是看了beyondvoid大牛才会得。。。关键在第一步转化。

首先,Ai^2可以看成对于相同输出序列的结果中,进行一个N^2的枚举可得。

即:

假设Ai=n

那么

for (int i=1;i<=n;i++)

for (int j=1;j<=n;j++) ans++;

得出的ans就是Ai^2

所以就相当于走两个相同输出序列的方案数。

这样的话:

用f[x1,y1,x2,y2]表示X状态走到(x1,y1),Y状态走到(x2,y2),它们之前走的输出序列是相同的这种情况下的方案数。

((x1,y1)表示上面已经弹了x1颗珠子出来,下面已经弹出了y1颗珠子出来。)

所以有

f[x1,y1,x2,y2]=以下各项之和。

f[x1-1,y1,x2-1,y2]      前提是(u[x1]==u[x2])

f[x1-1,y1,x2,y2-1]      前提是(u[x1]==d[y2])

f[x1,y1-1,x2-1,y2]      前提是(d[y1]==u[x2])

f[x1,y1-1,x2,y2-1]      前提是(d[y1]==d[y2])

然后初始值f[0,0,0,0]=1

然后这样由于空间太吓人了,而状态X和状态Y是同步运动的,我们可以通过x1+y1-x2算出y2,于是就把第四维略去了。

三维的空间本来是可以过了,然后由于这个题库太犀利。。。数组开大了会变成system error,所以只得再搞一个滚动数组。然后没想到的是,位运算居然那么耗时,TLE掉了,然后我就先用变量把i&1和(i&1)^1存起来再做。至于里面呢,用减法代替取余是必须的,然后我看到用了好几次同一个f,每次j+1,k+1之流也许会变慢,于是我直接开指针把它表示出来。

【其它】不错,排到了第二。

Rank Run ID User Memory Time Language Code Length Submit Time 1 6774 moon5ckq 2088K 1029MS G++ 0.87K 2009-09-14 09:42:34 2 9615 edward_mj 2068K 1045MS G++ 1.42K 2010-02-19 01:16:37

【CODE】

#include