1334:【例2-3】围圈报数 |
这道题重点是关系的转换和初始化
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int a[101];//记录接着的的那个节点
int n,m;
int main(){
cin>>n>>m;
for(int i=1;i<n;i++) a[i]=i+1;
a[n]=1;
int p=0,head=1,j=n;//注意j的初值,因为一开始下面的写法
while(p<n){
while(head<m){
j=a[j];//接下
head++;
}
cout<<a[j]<<" "; //注意是接下来的那个退出,所以是a[j]
p++;
a[j]=a[a[j]];//退出
head=1;//重新数
}
return 0;
}
1335:【例2-4】连通块 |
很常见的一道题,经典
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m;
int map[101][101];
int dis[101][101];
int queue[10001][2];
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int ans;
void bfs(int x,int y){
int head=1;
int rear=2;//这是队列!!!这两个总的有
queue[1][0]=x;queue[1][1]=y;
while(head<rear){
x=queue[head][0];
y=queue[head][1];
head++;
for(int i=0;i<4;i++){
int x1=x+dir[i][0];
int y1=y+dir[i][1];
if(x1>0&&x1<=n&&y1>0&y1<=m&&map[x1][y1]&&!dis[x1][y1]){
dis[x1][y1]=1;
queue[rear][0]=x1;
queue[rear++][1]=y1;//入队
}
}
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) cin>>map[i][j];
memset(dis,0,sizeof(dis));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if(map[i][j]&&!dis[i][j]){
bfs(i,j);
ans++;
}
}
cout<<ans<<endl;
return 0;
}
1359:围成面积 |
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
using namespace std;
//这道题得思路很难想,从边缘开始搜索,标记没有被1包围的0使他们和 1 成为同样的节点,最后枚举剩余的0即可
int map[20][20];
int queue[101][2];
int dis[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
void bfs(int x,int y){
int head=1,tail=0;
map[x][y]=-1;//使这些0变为-1
queue[head][0]=x;
queue[head][1]=y;
while(head!=tail){
tail++;
for(int i=0;i<4;i++){
int nx=queue[tail][0]+dis[i][0];
int ny=queue[tail][1]+dis[i][1];
if(map[nx][ny]==-1) continue;
else map[nx][ny]=-1;
head++;
queue[head][0]=nx;
queue[head][1]=ny;
}
}
}
int main(){
int ans=0;
memset(map,255,sizeof(map)); //首先要先初始化map为-1 !!!!!(255=-1) //能够在搜索的时候退出
for(int i=1;i<=10;i++) //这里从1开始,是可以不用再bfs里面判断其下标合不合理
for(int j=1;j<=10;j++) {
cin>>map[i][j];
map[i][j]=-map[i][j];//注意这里的标记,既可以用来表示其代表的值,也可以用来表示有没有访问过
}//这里0还是0,1变为了-1
for(int i=1;i<=10;i++){
if(!map[i][1]) bfs(i,1);
if(!map[i][10]) bfs(i,10);
if(!map[1][i]) bfs(1,i);
if(!map[10][i]) bfs(10,i);//如果是0就开始搜索
}
for(int i=1;i<=10;i++)
for(int j=1;j<=10;j++) if(map[i][j]==0) ans++;//这个时候还有的0就是被包围的,因为从边缘搜索得到的0全部都被标记为了-1
cout<<ans<<endl;
return 0;
}
1360:奇怪的电梯(lift) |
大楼的每一层楼都可以停电梯,而且第i层楼(1≤i≤N)(1≤i≤N) 上有一个数字Ki(0≤=Ki≤=N)Ki(0≤=Ki≤=N) 。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如: |
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
using namespace std;
int pre[10001],aa[10001],vis[10001];
int sum[10001];
//pre表示当前的楼层(队列),a表示能上升和下降的数量
//vis表示是否经过(保证最小距离)sum表示所用的次数!!!!(useful)
int n,a,b;
int main(){
cin>>n>>a>>b;
for(int i=1;i<=n;i++) cin>>aa[i];
int head=0,tail=1;
pre[tail]=a;
memset(vis,0,sizeof(vis));
vis[a]=1;//确保只能去一次
do{
head++;
for(int i=-1;i<=1;i+=2){//模拟上下两次选择
if(pre[head]+i*aa[pre[head]]>=1&&pre[head]+i*aa[pre[head]]<=n&&!vis[pre[head]+i*aa[pre[head]]]){
tail++;
pre[tail]=pre[head]+i*aa[pre[head]];
vis[pre[head]+i*aa[pre[head]]]=1;
sum[tail]=sum[head]+1;//这个数列的作用就相当于queue[n][3]
}
}
}while(head<tail&&pre[head]!=b);//到达目标在这里判断
if(a==b) cout<<0<<endl;
else if(pre[tail]==b) cout<<sum[tail]<<endl;
else cout<<"-1"<<endl;
return 0;
}
1361:产生数(Produce) |
没有想到在一本通也有。。。。在洛谷上面做的数据更大一些,必须得换一个方法不然会爆掉
一本通:
给出一个整数n(n≤2000)和k个变换规则(k≤15)。 |
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<queue>
#include<cstdio>
using namespace std;
int n,k;
int be[16],af[16];
int vis[10001];
queue<int> qu;
int main(){
cin>>n>>k;
memset(vis,0,sizeof(vis));
for(int i=1;i<=k;i++) cin>>be[i]>>af[i];
qu.push(n);
vis[n]=1;
int sum=1;
while(!qu.empty()){
int x=qu.front(),y=qu.front();
cout<<qu.front()<<endl;
qu.pop();
int mod=1;
while(x>0){
int temp=x%10;
x/=10;
for(int i=1;i<=k;i++){
if(be[i]==temp){
int yy=y+(af[i]-be[i])*mod;//因为是一位一位处理的
if(!vis[yy]){
sum++;
vis[yy]=1;
qu.push(yy);
}
}
}
mod*=10;
}
}
cout<<sum<<endl;
return 0;
}
洛谷上面:
数据是会超过long long的,所以必须用高精度
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
using namespace std;
//产生数
/*
很容易看出这是一道搜索题
考虑搜索方案,如果按字符串转移,必须存储每种状态,空间复杂度明显会爆炸
观察到每一位之间是互不影响的,考虑使用乘法原理
搜索出每一位的情况总数,求它们的连乘积即为答案!!!!!!注意数据很大
时间复杂度O(n2^k)O(n2k)
可以看出答案最大可以达到三十的十次方,会爆掉longlong longlong,所以需要写高精
具体处理可以选择STL(懒得自己写)
对于映射,这是map的专长
如果一个数能够映射到多个数呢?
用map的时候从charchar映射到vector<char>vector<char>即可
*/
map<char,vector<char> > st;
int c[10],num[111],k;
string a;
//用来判断数字出现个数的,和记录结果的
void dfs(char z){
c[z-'0']=1;
int size=st[z].size();
for(int i=0;i<size;i++){
if(!c[st[z][i]-'0']){
dfs(st[z][i]);
}
}
}
int main(){
cin>>a;
cin>>k;
for(int i=1;i<=k;i++){
char o,p;
cin>>o>>p;
st[o].push_back(p);
}
//我根本就没有考虑到一个数字可以有多种变换
num[0]=1;
int l=a.length();
//接下来计算每一位的变换次数进行连乘
for(int i=0;i<l;i++){
memset(c,0,sizeof(c)); //!!!!!写法每次循环都清空
dfs(a[i]);
int summ=0;
for(int j=0;j<=9;j++){
summ+=c[j]; //summ表示这次变换得前面可以有多少种变化
}//高精度乘法
int x=0;
for(int j=0;j<100;j++){
num[j]=num[j]*summ+x;
x=num[j]/10;
num[j]%=10;
}
}
int i=100;
while(num[i]==0&&i>0) i--;
for(;i>=0;i--) cout<<num[i];
return 0;
}
家庭问题(并查集的题目)
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<queue>
#include<cstdio>
using namespace std;
//这个是并查集
//操作:合+并
int father[101];
bool isroot[101];
int sum[101];
int n,k;
int findfather(int x){
int a=x;
while(x!=father[x]){
x=father[x];
}
while(a!=father[a]){//路径压缩
int z=a;
a=father[a];//把路径上的点的father都赋值为根节点
father[z]=x;
}
return x;
}
void Un(int a,int b){
int faA=findfather(a);
int faB=findfather(b);
if(faA!=faB) father[faA]=faB;
}
void inti(int n){
for(int i=1;i<=n;i++){
father[i]=i;//初始化,每个都是独立的
isroot[i]=0;
}
}
int main(){
cin>>n>>k;
inti(n);
int xx,yy;
for(int i=1;i<=k;i++) {
cin>>xx>>yy;
Un(xx,yy);
}
for(int i=1;i<=n;i++){
isroot[findfather(i)]=1;//是根节点的赋值为1
sum[findfather(i)]++; //根节点的家庭成员++
}
int ans=0,maxn=-1;
for(int i=1;i<=n;i++){
ans+=isroot[i];
maxn=max(maxn,sum[i]);
}
cout<<ans<<" "<<maxn<<endl;
return 0;
}