2003 Rocky Mountain Regional 题解

POJ 2316 SPIN

题意简述:有一个密码锁,每次对密码锁每个轮盘进行操作,问最终状态。

很简单的模拟。。。

时间复杂度 O(nm),n是密码锁大小,m是操作次数。

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
int stat[110];
char buf[110];
int main(){
    int l=0;
    while(scanf("%s",buf)!=EOF){
        if(l==0) l=strlen(buf);
        for(int i=0;i<l;i++){
            stat[i]=(stat[i]+buf[i]-'0')%10;
        }
    }
    for(int i=0;i<l;i++) printf("%d",stat[i]);
    printf("\n");
    return 0;
}
AC代码

POJ 2317 SHAKE

题意简述:给一段文本,将其全部转大写后放入矩阵然后进行移动操作以加密文本,输出加密结果。

有点复杂的模拟,这些操作我是递归实现的,注意判断什么时候终止就行。我是嫌麻烦才递归,一般用循环更好

时间复杂度 O(mn^2),m 是操作次数,n 是矩阵大小。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;
typedef long long ll;
char buf[10010],head[110];
int n;
char &arr(int i,int j){
    return buf[(i-1)*n+j-1];
}
void shake(int i,int j){
    if(j%2==1){
        char c=arr(i%n+1,j);
        if(i==2) arr(i,j)=c;
        else {
            shake((i+n-2)%n+1,j);
            arr(i,j)=c;
        }
    } else {
        char c=arr((i+n-2)%n+1,j);
        if(i==n) arr(i,j)=c;
        else {
            shake(i%n+1,j);
            arr(i,j)=c;
        }
    }
}
void rattle(int i,int j){
    if(i%2==0){
        char c=arr(i,j%n+1);
        if(j==2) arr(i,j)=c;
        else {
            rattle(i,(j+n-2)%n+1);
            arr(i,j)=c;
        }
    } else {
        char c=arr(i,(j+n-2)%n+1);
        if(j==n) arr(i,j)=c;
        else {
            rattle(i,j%n+1);
            arr(i,j)=c;
        }
    }
}
void rotate(int i,int j,int di,int dj,int t){
    if(t==-1){
        t=i%2;
        if(t==1){
            di=0;
            dj=1;
        } else {
            di=1;
            dj=0;
        }
        rotate(i+di,j+dj,di,dj,t);
        return;
    }
    char c=arr(i-di,j-dj);
    if(i==j&&i<=n/2&&t!=-1){
        arr(i,j)=c;
        return;
    }
    if(i==j||i+j==n+1){
        if(t==0){
            int ddi=di;
            di=-dj;
            dj=ddi;
        } else {
            int ddi=di;
            di=dj;
            dj=-ddi;
        }
    }
    rotate(i+di,j+dj,di,dj,t);
    arr(i,j)=c;
}
int main(){
    while(scanf("%s",head)!=EOF){
        getchar();
        scanf("%[^\n]",buf);
        n=(head[0]-'0')*10+head[1]-'0';
        if(n==0) n=100;
        char ch='A';
        for(int i=strlen(buf)-1;i>=0;i--){
            if(islower(buf[i])) buf[i]-=32;
        }
        for(int i=strlen(buf);i<n*n;i++){
            buf[i]=ch;
            ch+=1;
            if(ch=='Z'+1) ch='A';
        }
        buf[n*n]='\0';
        int l=strlen(head);
        for(int i=2;i<l;i++){
            if(head[i]=='S') for(int i=1;i<=n;i++) shake(1,i);
            else if(head[i]=='R') for(int i=1;i<=n;i++) rattle(i,1);
            else for(int i=1;i<=n/2;i++) rotate(i,i,0,0,-1);
        }
        printf("%s\n",buf);
    }
}
AC代码

POJ 2318 TOYS

题意简述:一个盒子被隔板分成若干个小格,给出一些点,问这些点处于哪个格子中。

作出点到隔板的两个端点的向量,作出下面的向量叉乘上面的向量的结果。

如图所示,这样如果隔板在点的右边(红色向量),那么右边那个角小于180度,叉乘为正;反之如果隔板在点的左边(蓝色向量),那么右边那个角大于 180 度,叉乘为负。这样就可以二分查找点所在的位置。

时间复杂度 O(mlog n)。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
struct bd {
    int u,d;
} arr[5010];
int cnt[5010];
int main(){
    int cn,n,m,xl,xr,yu,yd;
    bool rt=false;
    while(scanf("%d",&n)!=EOF&&n!=0){
        if(rt) printf("\n");
        else rt=true;
        memset(cnt,0,sizeof(cnt));
        scanf("%d%d%d%d%d",&m,&xl,&yu,&xr,&yd);
        for(int i=0;i<n;i++){
            scanf("%d%d",&arr[i].u,&arr[i].d);
        }
        arr[n].u=arr[n].d=xr;
        for(int i=0;i<m;i++){
            int l=0,r=n;
            int x,y;
            scanf("%d%d",&x,&y);
            while(l!=r){
                int mid=(l+r)/2;
                if((arr[mid].d-x)*(yu-y)-(arr[mid].u-x)*(yd-y)>0) r=mid;
                else l=mid+1;
            }
            ++cnt[l];
        }
        for(int i=0;i<=n;i++) printf("%d: %d\n",i,cnt[i]);
    }
    return 0;
}
AC代码

POJ 2319 COMPRESS

题意简述:给出两个数,按给出的方案将数字简化表示,如 1234-1256 表示为 1234-56,4321-4411 表示为 4321-11。

可以发现压缩后第二个数永远是压缩前第二个数的后几位,因此从小到大试出取几位可以得到正确结果即可。如果你想的话还可以写个二分,反正我没写

时间复杂度 O(mlog b),m 是测试数据组数,b 是第二个数。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;
typedef long long ll;
char fmt[]="%lld-%000lld\n";
int main(){
    ll a,b;
    while(scanf("%lld-%lld",&a,&b)!=EOF){
        ll d=0,n=1,r=-1;
        while(b!=r){
            d+=1;
            n*=10;
            if(b%n<=a%n){
                r=a/n*n+n+b%n;
            } else {
                r=a/n*n+b%n;
            }
        }
        fmt[7]='0'+d/10;
        fmt[8]='0'+d%10;
        printf(fmt,a,b%n);
    }
    return 0;
}
AC代码

POJ 2320 SHEET

题意简述:给出excel里面若干单元格的定义,判断每个格子会不会因为循环定义而无法计算出来。

根据每个格子的公式可以求出这个格子依赖于哪些格子,然后可以建一张有向图出来。在这张有向图上,不停的找入度为 0 的节点,表示这个单元格不依赖其他格或者依赖的格都能计算出来,然后删去这个点的所有出边。实际操作不需要真的删边,只需要把入度为 0 的节点所有出边都减 1 就行了。

每条边最多只删一次,因此时间复杂度 O(m)。

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
int head[410],tot,cell[410],deg[410],vis[410];
struct edge {
    int to,next;
} G[500010];
int no[10000],ntot;
char buf[100010];
void addedge(int x,int y){
    G[++tot].to=y;
    G[tot].next=head[x];
    head[x]=tot;
    ++deg[y];
}
void dfs(int x){
    if(vis[x]) return;
    vis[x]=true;
    for(int it=head[x];it;it=G[it].next){
        int y=G[it].to;
        --deg[y];
        if(deg[y]==0) dfs(y);
    }
}
int main(){
    while(scanf("%s",buf)!=EOF){
        int x=buf[1]*1000+buf[2]*100+buf[4]*10+buf[5]*1-53328;
        if(no[x]==0) {no[x]=++ntot;cell[ntot]=x;}
        x=no[x];
        int l=strlen(buf);
        for(int i=7;i<l;i++){
            if(buf[i]=='R'){
                int y=buf[i+1]*1000+buf[i+2]*100+buf[i+4]*10+buf[i+5]*1-53328;
                if(no[y]==0) {no[y]=++ntot;cell[ntot]=y;}
                y=no[y];
                addedge(y,x);
            }
        }
    }
    for(int i=1;i<=ntot;i++){
        if(deg[i]==0) dfs(i);
    }
    for(int i=1;i<=ntot;i++){
        if(deg[i]) printf("R%02dC%02d circular\n",cell[i]/100,cell[i]%100);
        else printf("R%02dC%02d ok\n",cell[i]/100,cell[i]%100);
    }
}
AC代码

POJ 2321 GYM

题意简述:每个篮子里面有一些卡片指示下一个地址,每次随机从篮子中选一张卡,从第一个篮子出发,问 10 轮内每轮停在每个位置的概率。

模拟就行了,一开始第一个位置的概率为 1 ,其他位置为 0,令第 i 个位置指示第 j 的卡片有 c(i,j) 张,第 i 轮在第 j 个位置的概率为 p(i,j) ,则 p(i,j) = Σ[k=1..n]( p(i-1,k) * c(k,j) / Σ[x=1..n]c(k,x) )

时间复杂度 O(n^2)

#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <sstream>
//#include <cstring>
using namespace std;
typedef long long ll;
int arr[50][50];
double pos[50][50],sum[50];
string str;
int main(){
    getline(cin,str);
    int n=0;
    istringstream sin(str);
    while(sin>>arr[0][n++]);
    --n;
    for(int i=1;i<n;i++){
        for(int j=0;j<n;j++){
            cin>>arr[i][j];
        }
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            sum[i]+=arr[i][j];
        }
    }
    pos[0][0]=1;
    for(int i=0;i<n;i++){
        printf("%.5f%c",pos[0][i],i==n-1?'\n':' ');
    }
    for(int ii=1;ii<10;ii++){
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                pos[ii][j]+=pos[ii-1][i]*arr[i][j]/sum[i];
            }
        }
        for(int i=0;i<n;i++) printf("%.5f%c",pos[ii][i],i==n-1?'\n':' ');
    }
}
AC代码

POJ 2322 PLANKS

题意简述:在一个 10*10 的矩阵上有一些木桩,有若干长度小于 10 的木板,木板的两端必须放在木桩上,且不允许斜放。问是否有方案能从 (1,1) 到达 (10,10) 。

如果在 (x,y) 有一个长度为 l 的木板,那么能达到的点就只有 (x+l,y), (x-l,y), (x,y+l), (x,y-l). 这样我们就可以判断这些地方有没有木桩,然后dfs。

时间复杂度理论上可达 O(m!), m 是木板的数量,但这题这样就已经能过了(

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;
typedef long long ll;
char M[20][20];
bool vis[20][20];
int plk[50],n;
char msg[50][100],mc;
bool proc(int i,int j,int p,int d);
bool dfs(int i,int j,int d){
    if(i==9&&j==9) {
        mc=d;
        return true;
    }
    for(int p=0;p<n;p++){
        int pl=plk[p];
        if(pl==0) continue;
        plk[p]=0;
        if(proc(i+pl,j,p,d)) return true;
        if(proc(i-pl,j,p,d)) return true;
        if(proc(i,j+pl,p,d)) return true;
        if(proc(i,j-pl,p,d)) return true;
        plk[p]=pl;
    }
    return false;
}
bool proc(int i,int j,int p,int d){
    if(!(i>=0&&i<10&&j>=0&&j<10&&M[i][j]=='*'&&!vis[i][j])) return false;
    vis[i][j]=true;
    bool r=dfs(i,j,d+1);
    if(r) sprintf(msg[d],"place plank %d to stump (%d,%d)",p+1,i+1,j+1);
    vis[i][j]=false;
    return r;
}
int main(){
    for(int i=0;i<10;i++){
        scanf("%s",M[i]);
    }
    bool rt=false;
    vis[0][0]=true;
    while(scanf("%d",&n)!=EOF){
        if(rt) printf("\n");
        rt=true;
        for(int i=0;i<n;i++){
            scanf("%d",&plk[i]);
        }
        if(dfs(0,0,0)){
            for(int i=0;i<mc;i++){
                printf("%s\n",msg[i]);
            }
        } else {
            printf("no solution possible\n");
        }
    }
    return 0;
}
AC代码

POJ 2323 PERMS

题意简述:问长度为 n,逆序对数量为 k 的 1-n 的排列有多少个。

这大概是这场唯一不是暴力求解的题吧(

令 dp[i][j] 表示长度为 i ,逆序对数量为 j 的排列的个数。

那么考虑如何转移。以 m 结尾的,长度为 i ,有 j 个逆序对的排列的数量等于长度为 i-1 ,有 j-(i-m) 个逆序对的数量。这是因为m和前面的数总是会形成 i-m 个逆序对。

例如之前的序列为 2 1 3,那么如果长度为 4 的序列以 2 结尾时,转移后的新序列为 3 1 4 2,2 和前面的 3 和 4 形成两个新的逆序对。

不太明白的可以看看我的AC代码是如何转移的。

时间复杂度 O(n^2*k+m),n=18,k=200(就是询问的n和k的上界),m 是测试数据的组数。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;
typedef long long ll;
ll dp[20][210];
int main(){
    dp[1][0]=1;
    for(int i=2;i<=18;i++){
        for(int j=0;j<=200;j++){
            for(int k=1;k<=i;k++){
                if(j-(i-k)>=0) dp[i][j]+=dp[i-1][j-(i-k)];
            }
        }
    }
    int a,b;
    while(scanf("%d%d",&a,&b)!=EOF&&a!=0){
        printf("%lld\n",dp[a][b]);
    }
    return 0;
}
AC代码

 

posted @ 2020-08-21 12:15  whatss7  阅读(341)  评论(0)    收藏  举报