HDU_Steps9.2 杂题 HDU2052 HDU1871 HDU1254 HDU3350 HDU3234 HDU2642 HDU2833 HDU3483

HDU2052 Picture

HDU1871 无题

上面两道纯水题。。为什么Steps做到这中间还有这么水的题=。=

HDU1254 推箱子

推箱子模型,最短路使用DFS加回溯,更新找到箱子的最小值,搜索人可达的位置用BFS,人和箱子的位置共同组成一个状态。

#include<cstdio>
#include<string.h>
#include<queue>
using namespace std;
int cas,m,n,sr,sc,er,ec,pr,pc,ans;
bool flag;
int map[10][10],dvis[10][10][10][10];
int dr[]={0,-1,0,1};
int dc[]={1,0,-1,0};
bool outr(int x){
	return x<0||x>=m;
}
bool outc(int x){
	return x<0||x>=n;	
}
//传入新旧人的位置以及箱子的位置
bool bfs(int opr,int opc,int npr,int npc,int a,int b){
	if(opr==npr&&opc==npc)return true;
	queue<pair<int,int> > q;
	int bvis[10][10];
	memset(bvis,0,sizeof bvis);
	q.push(make_pair(opr,opc));
	bvis[opr][opc]=1;
	while(!q.empty()){
		int r=q.front().first,c=q.front().second;
		q.pop();
		for(int i=0;i<4;i++){
			int nr=r+dr[i],nc=c+dc[i];
			if(nr<0||nc<0||nr>=m||nc>=n||bvis[nr][nc])continue;
			//墙和箱子的位置是不可走的
			if(map[nr][nc]==1||(nr==a&&nc==b))continue;	
			bvis[nr][nc]=1;
			if(nr==npr&&nc==npc)return true;
			q.push(make_pair(nr,nc));
		}
	}
	return false;	
}
void dfs(int obr,int obc,int opr,int opc,int step){//上一步箱子和人的位置,以及走的步数
	if(step>ans)return;//这里注意剪枝
	if(obr==er&&obc==ec){
		ans=min(step,ans);
		flag=true;//标记成功到达
		return;	
	}
	for(int i=0;i<4;i++){
		//算出新的人和新的箱子的位置
		int npr=obr+dr[i],npc=obc+dc[i];	
		int nbr=obr+dr[(i+2)%4],nbc=obc+dc[(i+2)%4];
		
		if(outr(nbr)||outr(npr)||outc(nbc)||outc(npc))continue;//出界了
		if(map[nbr][nbc]==1||map[npr][npc]==1)continue;//人或者箱子要去的位置有墙
		if(!bfs(opr,opc,npr,npc,obr,obc))continue;//人不能走到那个位置
		if(dvis[npr][npc][nbr][nbc]==1)continue;//已经到达过这个状态
		
		dvis[npr][npc][nbr][nbc]=1;
		dfs(nbr,nbc,npr,npc,step+1);
		dvis[npr][npc][nbr][nbc]=0;
	}
	return;
}
int main(){
	scanf("%d",&cas);
	while(cas--){
		scanf("%d%d",&m,&n);	
		for(int i=0;i<m;i++){
			for(int j=0;j<n;j++){
				scanf("%d",&map[i][j]);	
				if(map[i][j]==2)sr=i,sc=j;
				if(map[i][j]==4)pr=i,pc=j;
				if(map[i][j]==3)er=i,ec=j;
			}	
		}
		flag=false;
		ans=10000000;
		memset(dvis,0,sizeof dvis);
		dfs(sr,sc,pr,pc,0);//传入箱子和人的位置
		if(flag==false){
			printf("-1\n");	
		}else{
			printf("%d\n",ans);
		}
	}
	return 0;	
}


HDU3350 #define is unsafe

给定一个只含有MAX和+操作的式子,求加法运行了多少次,其中MAX使用宏定义。注意一个规律,对于MAX(A,B)其中A中加a次,B中加b次若A>B,则加a*2+b次,否则a+b*2次。然后用递归处理这个字符串就可以了。

#include<cstdio>
#include<string>
#include<iostream>
using namespace std;
struct state{
	state(int a,int b){s=a,k=b;}
	int s,k;//和,次数
};
state find(string str){
	int in=0,len=str.length(),num=0;//当前位置,字符串长度

	if(str[in]>='0'&&str[in]<='9'){//第一个字母是整数,读取这个数
		while(in<len&&str[in]>='0'&&str[in]<='9')num=num*10+str[in++]-'0';
		if(in>=len)return state(num,0);//如果只剩一个数,直接返回
		else{//只能是a+B的形式,B是一个式子,处理B段
			state st=find(str.substr(in+1));
			return state(num+st.s,st.k+1);
		}
	}else if(str[in]=='M'){
		in+=4;
		int cnt=1,mid=0;
		while(cnt>0){//匹配MAX()右括号的位置,并找出对应这个MAX的逗号的位置
			if(str[in]=='(')cnt++;
			else if(str[in]==')')cnt--;
			if(str[in]==','&&cnt==1)mid=in;	
			in++;
		}
		state le=find(str.substr(4,mid-4));//求MAX(A,B)中的A
		state ri=find(str.substr(mid+1,in-mid-2));//求MAX(A,B)中的B
		int p1,p2;
		if(le.s>ri.s){
			p1=le.s;
			p2=le.k*2+ri.k;	
		}else{
			p1=ri.s;
			p2=le.k+ri.k*2;
		}
		if(in>=len-1){//已经到达末端
			return state(p1,p2);	
		}else{//MAX(A,B) + C的形式,求C
			state st=find(str.substr(in+1));//处理加号后面的部分
			return state(p1+st.s,st.k+p2+1);
		}
	}	 
}
int main(){	
	int cas;
	char str[1005];
	cin>>cas;
	while(cas--){
		cin>>str;
		state rs=find(str);
		cout<<rs.s<<" "<<rs.k<<endl;
	}
	return 0;	
}

HDU3234 Exclusive-OR

这题并查集真恶心啊。。首先a^a=0,a^0=a,虚拟一个父节点,将所有已知的连接到这个父节点上来,用一个数组记录该节点异或父节点的值,最后如果是偶数个则父节点被异或偶数次,不影响结果,如果为奇数次并且父节点不是虚拟节点的话就无法得到答案了。如果在同一个集合中但是结果与当前值冲突就是wrong了~

#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
int n,q;
int p[20005],va[20005],map[20005];
void init(int x){
    for(int i=0;i<=x;i++){
        p[i]=i;
        va[i]=0;
    }    
}
int find(int x){
    int t=p[x];
    if(x!=p[x]){
        p[x]=find(p[x]);
        va[x]^=va[t];//x^p[x]=va[x],p[x]^p[px]=va[p[x]],所以x^p[p[x]]=va[x]^va[p[x]]
    }
    return p[x];    
}
bool merge(int x,int y,int v){
    int px=find(x);
    int py=find(y);
    if(px==py){
        if((va[x]^va[y])!=v)return false;
        return true;    
    }
    if(px==n)swap(px,py);//n为虚拟节点,已知的都连到n上
    p[px]=py;
    //x^px=va[x],y^py=va[y],x^py=v;
    //所以px^py=va[x]^va[y]^v
    va[px]=va[x]^va[y]^v;
    p[px]=py;
    return true;
}


int main(){
    char op,s[1000],cas=1;
	int a[20];
    while(scanf("%d%d",&n,&q),n||q){
        gets(s);
        init(n+1);
        printf("Case %d:\n",cas++);
        int nqs=0,tmpas,num;
        bool right=true;
        while(q--){
            gets(s);
            tmpas=0,num=0;
            op=s[0];
            //不正确就可以结束了
            if(!right)continue;
            int len=strlen(s);
            for(int i=2;i<len;i++){//输入数字,数字数量不确定
                if(s[i]>='0'&&s[i]<='9')num=num*10+s[i]-'0';
                else{
                    a[tmpas++]=num;
                    num=0;
                }
            }
            a[tmpas++]=num;
            if(op=='I'&&tmpas==2){//I p v,p和n合并
                right=merge(a[0],n,a[1]);
                nqs++;
            }else if(op=='I'&&tmpas>2){
                right=merge(a[0],a[1],a[2]);
                nqs++;
            }else if(op=='Q'){
                int ans=0;
                memset(map,0,sizeof map);
                for(int i=1;i<tmpas;i++){//a^a=0这种直接排除
                    map[find(a[i])]++;
                    ans=(ans^va[a[i]]);
                }
                bool f=1;
                for(int i=1;i<tmpas;i++){
                    int t=find(a[i]);
                    if(map[t]%2!=0&&t!=n){//假设x和y的和都是t,x^t^y^t=t^y,t被抵消掉,但如果奇数次,t未知并且未被抵消就不能得到答案
                        printf("I don't know.\n");
                        f=0;
                        break;
                    }
                }
                if(f){
                    printf("%d\n",ans);
                }
            }
            if(!right){
                printf("The first %d facts are conflicting.\n",nqs);
            }
        }
        printf("\n");
    }
    return 0;    
}

HDU2642 Stars 

裸的二维树状数组加上简单的容斥原理

#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
int c[1002][1002];
bool v[1002][1002];
int lowbit(int x){
	return x&(-x);
}
void modify(int x,int y,int p){
	for(int i=x;i<=1001;i+=lowbit(i)){
		for(int j=y;j<=1001;j+=lowbit(j)){
			c[i][j]+=p;	
		}	
	}	
}
int sum(int x,int y){
	int s=0;
	for(int i=x;i>0;i-=lowbit(i)){
		for(int j=y;j>0;j-=lowbit(j)){
			s+=c[i][j];	
		}	
	}
	return s;
}
int main(){
	int m,x1,x2,y1,y2;
	char op[3];
	while(scanf("%d",&m)!=EOF){
		memset(v,false,sizeof v);
		memset(c,0,sizeof c);
		while(m--){
			scanf("%s",op);
			if(op[0]=='B'){
				scanf("%d%d",&x1,&x2);
				x1++,x2++;
				if(v[x1][x2])continue;
				v[x1][x2]=true;	
				modify(x1,x2,1);
			}else if(op[0]=='D'){
				scanf("%d%d",&x1,&x2);
				x1++,x2++;
				if(!v[x1][x2])continue;
				v[x1][x2]=false;	
				modify(x1,x2,-1);
			}else{
				scanf("%d%d%d%d",&x1,&x2,&y1,&y2);
				x1++,x2++,y1++,y2++;
				if(x1>x2)swap(x1,x2);
				if(y1>y2)swap(y1,y2);	
				printf("%d\n",sum(x2,y2)+sum(x1-1,y1-1)-sum(x1-1,y2)-sum(x2,y1-1));
			}
		}
	}
	return 0;	
}


HDU2833 WuKong

好题,求两条最短路中最多能公共几个点。首先容易证明公共最短路必然连续,然后用floyd求出每两点之间的最短路。如果d[st][en]=d[st][i]+d[i][j]+d[j][en],说明d[i][j]是最短路的一部分。c[i][j]代表两点之间最短路的条数。

#include<cstdio>
#include<algorithm>
using namespace std;
const int inf=200000000;
int d[305][305],c[305][305];
//有一个性质,如果有公共最短路,则公共最短路一定是连续的(可以用反证法证明)
//Floyd后,如果i和j在最短路上则有d[s][e]=d[s][i]+d[i][j]+d[j][e]
int main(){
	int n,m,u,v,w,s1,s2,t1,t2;
	while(scanf("%d%d",&n,&m),n||m){
		for(int i=0;i<=n;i++){
			for(int j=0;j<=n;j++){
				d[i][j]=(i==j)?0:inf;
				c[i][j]=0;
			}	
		}
		for(int i=0;i<m;i++){
			scanf("%d%d%d",&u,&v,&w);
			if(d[u][v]<w)continue;
			d[u][v]=d[v][u]=w;
			c[u][v]=c[v][u]=1;
		}
		scanf("%d%d%d%d",&s1,&t1,&s2,&t2);
		//Folyd,其中c[i][j]表示两点间最短路的条数最大有多少条
		for(int k=1;k<=n;k++){
			for(int i=1;i<=n;i++){
				for(int j=1;j<=n;j++){
					if(d[i][j]>d[i][k]+d[k][j]){
						d[i][j]=d[i][k]+d[k][j];	
						c[i][j]=c[i][k]+c[k][j];
					}	
					if(d[i][j]==d[i][k]+d[k][j]&&c[i][j]<c[i][k]+c[k][j]){
						c[i][j]=c[i][k]+c[k][j];
					}
				}	
			}	
		}
		//DP
		int rs=-1;//可能只有一个交点;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if(d[s1][i]+d[i][j]+d[j][t1]==d[s1][t1]&&d[s2][i]+d[i][j]+d[j][t2]==d[s2][t2]){
					rs=max(rs,c[i][j]);	
				}	
			}
		}
		printf("%d\n",rs+1);
	}
	return 0;	
}


HDU3483 A Very Simple Problem

求sigma(x^k*k^x),k<2*1e9,x<50,又是一道恶心的构造矩阵,解释在注释中

#include<cstdio>
#include<string.h>
using namespace std;
/*求sum(x^k*k^x) k=1~N
 * x^(k+1)*(k+1)^x=x^k*x*(k+1)^x 然后用二项式定理展开(k+1)^x即可
 * 例如当x=4时  
 * | 1x  0  0  0  0  0 | |x^k*k^0| |x^(k+1)*(k+1)^0|
 * | 1x 1x  0  0  0  0 | |x^k*k^1| |x^(k+1)*(k+1)^1|
 * | 1x 2x 1x  0  0  0 |*|x^k*k^2|=|x^(k+1)*(k+1)^2|
 * | 1x 3x 3x 1x  0  0 | |x^k*k^3| |x^(k+1)*(k+1)^3|
 * | 1x 4x 6x 4x 1x  0 | |x^k*k^4| |x^(k+1)*(k+1)^4|
 * | 1x 4x 6x 4x 1x 1x | | S(k)  | |     S(k+1)    |
 */
typedef __int64 ll;
const int maxn=55;
ll c[maxn][maxn],n,m,x;
void init(){//初始化二项式系数
	memset(c,0,sizeof c);
	for(int i=1;i<maxn;i++){
		c[i][1]=c[i][i]=1;
		for(int j=2;j<=i-1;j++){
			c[i][j]=c[i-1][j-1]+c[i-1][j];
			if(c[i][j]>=m)c[i][j]%=m;
		}
	}
}
struct mat{
	ll a[maxn][maxn];
	mat(int type){//0是全0矩阵,2是单位矩阵,1是构造的矩阵
		memset(a,0,sizeof a);
		if(type==0)return;
		if(type==2){
			for(int i=0;i<x+2;i++)a[i][i]=1;
			return;		
		}
		for(int i=0;i<x+2;i++){
			for(int j=0;j<=i;j++){
				if(i!=x+1){
					a[i][j]=c[i+1][j+1]*x;
					if(a[i][j]>=m)a[i][j]%=m;
				}else a[i][j]=a[i-1][j];
			}
		}	
		a[x+1][x+1]=1;	
	}
	mat mult(mat b){
		mat rs(0);
		for(int i=0;i<x+2;i++){
			for(int j=0;j<x+2;j++){
				for(int k=0;k<x+2;k++){
					rs.a[i][j]=rs.a[i][j]+a[i][k]*b.a[k][j];
					if(rs.a[i][j]>=m)rs.a[i][j]%=m;
				}	
				
			}	
		}	
		return rs;
	}
	int getr(){
		ll r=0;
		for(int i=0;i<x+2;i++){
			r+=x*a[x+1][i];
		}	
		return r%m;
	}
};
mat binMat(int k){//非递归的二分求解矩阵,递归栈溢出
	mat m(1),tmp(2);
	while(k){
		if(k&1)tmp=tmp.mult(m);
		m=m.mult(m);
		k>>=1;
	}
	return tmp;
}
int main(){
	while(scanf("%I64d%I64d%I64d",&n,&x,&m)){
		if(n==-1)break;
		init();
		mat m=binMat(n-1);
		printf("%d\n",m.getr());
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值