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; }
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); } }
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; }
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; }
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); } }
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':' '); } }
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; }
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; }

浙公网安备 33010602011771号