zoukankan      html  css  js  c++  java
  • 2019.8.18小结

    T1 邮递员送信 100/100

    题意

    有一个邮递员要送东西,邮局在结点1。他总共要送N-1样东西,其目的地分别是2~N。由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有M条道路,通过每条道路需要一定的时间。这个邮递员每次只能带一样东西。求送完这N-1样东西并且最终回到邮局最少需要多少时间。

    很容易发现我们要求 (sum_{j=1}^n) dis[1][ j ]+dis[ j ][1],可以想到跑spfa,对于dis[1][ j ]可以一遍spfa解决但是对于 dis[ j ][1]却不好解决,如果跑n遍spfa明显超时,但我们可以换个思路,建立反图,对节点1跑spfa,那么1节点到别的点的距离就是别的点到1节点的距离。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    inline int read(){
        int x=0,f=1;
        char ch=getchar();
        while(ch<'0'||ch>'9'){
            if(ch=='-')
                f=-1;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=(x<<1)+(x<<3)+(ch^48);
            ch=getchar();
        }
        return x*f;
    }
    const int N=1010,M=100010;
    int head[N][2],ver[M][2],edge[M][2],Next[M][2],d[N][2];
    int n,m,tot1,tot2;
    long long ans;
    bool v[N];
    queue<int> q;
    void add(int x,int y,int z){
    	ver[++tot1][0]=y;edge[tot1][0]=z;Next[tot1][0]=head[x][0],head[x][0]=tot1;
    }
    void add1(int x,int y,int z){
    	ver[++tot2][1]=y;edge[tot2][1]=z;Next[tot2][1]=head[x][1],head[x][1]=tot2;
    }
    void spfa(){
    	memset(d,0x3f,sizeof(d));
    	d[1][0]=0,v[1]=1;
    	q.push(1);
    	while(q.size()){
    		int x=q.front();q.pop();
    		v[x]=0;
    		for(int i=head[x][0];i;i=Next[i][0]){
    			int y=ver[i][0],z=edge[i][0];
    			if(d[y][0]>d[x][0]+z){
    				d[y][0]=d[x][0]+z;
    				if(!v[y]) q.push(y),v[y]=1;
    			}
    		}
    	}
    	while(!q.empty()) q.pop();
    	memset(v,0,sizeof(v));
    	d[1][1]=0,v[1]=1;
    	q.push(1);
    	while(q.size()){
    		int x=q.front();q.pop();
    		v[x]=0;
    		for(int i=head[x][1];i;i=Next[i][1]){
    			int y=ver[i][1],z=edge[i][1];
    			if(d[y][1]>d[x][1]+z){
    				d[y][1]=d[x][1]+z;
    				if(!v[y]) q.push(y),v[y]=1;
    			}
    		}
    	}
    }
    int main(){
    	n=read();m=read();
    	for(int i=1;i<=m;++i){
    		int x,y,z;
    		x=read();y=read();z=read();
    		add(x,y,z);
    		add1(y,x,z);
    	}
    	spfa();
    	for(int i=1;i<=n;++i){
    		ans+=d[i][0]+d[i][1];
    	}
    	printf("%lld",ans);
    	return 0;
    }
    

    T2 敲砖块 70/100

    题意

    在一个凹槽中放置了N层砖块,最上面的一层油N块砖,从上到下每层一次减少一块砖。每块砖都有一个分值,敲掉这块砖就能得到相应的分值,如图所示。

    如果你想敲掉第i层的第j块砖的话,若i=1,你可以直接敲掉它;若i>1,则你必须先敲掉第i-1层的第j和第j+1块砖。

    你现在可以敲掉最多M块砖,求得分最多能有多少。

    一道dp题,一开始想到的是一行一行dp然而发现,选[ i , j ]就要选[ i-1 , j+1]和[ i ,j ]上面所有的方块,似乎不满足无后效性,那怎么办呢?
    我们发现输入文件时这样的

    4 5
    2 2 3 4
    8 2 7
    2 3
    49
    

    我们可以去思考是不是可以一列一列dp,从n列向1列dp这样就没有后效性了,我们可以定义状态f[i][j][k]表示当前在第i列选了j个,总共选了k个,状态转移方程为

    f[i][j][k]=max(f[i+1][t][k-j]+s[i][j],f[i][j][k])

    t>=j-1&&t<=n-i

    s[i][j]表示第j列前i个的和

    代码

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,ans,f[55][55][3000],a[55][55],s[55][55];
    int main(){
    	scanf("%d %d",&n,&m);
    	memset(f,-0x3f,sizeof(f));
        f[n+1][0][0]=0;
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<=n-i+1;++j){
    			scanf("%d",&a[i][j]);
    		}
    	}	
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<=n-i+1;++j){
    			s[j][i]=s[j][i-1]+a[i][j];
    		}
    	}
    	for(int i=n;i>=1;--i){
    		for(int j=0;j<=n-i+1;++j){
    			for(int k=j;k<=m;++k){
    				for(int t=max(j-1,0);t<=n-i;++t){
    					f[i][j][k]=max(f[i+1][t][k-j]+s[i][j],f[i][j][k]);
    				}
    			}
    		}
    	}
        for(int i=1;i<=n;++i){
        	for(int j=1;j<=n-i+1;++j){
        		ans=max(ans,f[i][j][m]);
        	}
        }
        printf("%d",ans);
    	return 0;
    }
    

    T3 等价表达式 30/0

    题意

    给一个表达式然后再给n个表达式,判断是否等价

    一道大模拟题,将a带为数,并且取模防止溢出

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=10007;
    char w[55],c[55];
    int a[100],t1,t2,x,n;
    long long ans[5],c1[55],c2[55],c3[55];
    int power(int x,int y){
    	int tmp=1;
    	while(y){
    		if(y&1) tmp=(tmp*x)%mod;
    		x=(x*x)%mod;
    		y>>=1;
    	}
    	return tmp%mod;
    }
    void clu(){
    	int m1=c1[t2],n1=c1[t2-1],m2=c2[t2],n2=c2[t2-1],m3=c3[t2],n3=c3[--t2]; 
    	switch(w[t1--]){
            case '+':c1[t2]=(n1+m1+mod)%mod; c2[t2]=(n2+m2+mod)%mod; c3[t2]=(n3+m3+mod)%mod; break;
            case '-':c1[t2]=(n1-m1+mod)%mod; c2[t2]=(n2-m2+mod)%mod; c3[t2]=(n3-m3+mod)%mod; break;
            case '*':c1[t2]=(n1*m1+mod)%mod; c2[t2]=(n2*m2+mod)%mod; c3[t2]=(n3*m3+mod)%mod; break;
            case '^':c1[t2]=power(n1,m1); c2[t2]=power(n2,m2); c3[t2]=power(n3,m3); break;
        }
    }
    void js(){
        x=0;
        for(int i=0;i<strlen(c);i++){
            if(c[i]=='a') {c1[++t2]=3;c2[t2]=7;c3[t2]=13;} 
            else if(isdigit(c[i])) x=x*10+c[i]-'0';
            else{
                if(x!=0){c1[++t2]=x;c2[t2]=x;c3[t2]=x;x=0;}
                if(a[c[i]]==0)continue;
                if(c[i]=='('||t1==0&&c[i]!=')') w[++t1]=c[i]; 
                else{ 
                    if(a[c[i]]<a[w[t1]]){
                    	if(c[i]!=')') w[++t1]=c[i]; 
    					else{ 
                            while(w[t1]!='('&&t1) clu();   
                            if(w[t1]=='(')t1--;
                        }
                    }  
                    else{
                        while(a[c[i]]>=a[w[t1]]&&t1) clu(); 
                        if(c[i]!=')') w[++t1]=c[i];
                    }
                } 
            }
        }
        if(x!=0){c1[++t2]=x;c2[t2]=x;c3[t2]=x;x=0;}
        while (t1)clu();
    }
    int main(){
    	a['(']=5;a[')']=1;a['^']=2;a['*']=a['/']=3;a['+']=a['-']=4;
    	gets(c);
    	scanf("%d",&n);
    	js();
    	gets(c);
    	ans[1]=c1[1];ans[2]=c2[1];ans[3]=c3[1];
    	for(int i=1;i<=n;++i){
            t1=t2=0;gets(c);js(); 
            if(ans[1]==c1[1]%mod&&ans[2]==c2[1]%mod&&ans[3]==c3[1]%mod) 
            printf("%c",i+64);
    	}
    	return 0;
    } 
    

    T4 扩散 100/100

    题意

    先讲一下一种容易陷入误区错误思路

    要使时间最小,就去找相对于每个点的最短曼哈顿距离,然后取最大值,时间就是(maxn+1)/2。

    代码

    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<cstdio>
    #include<iostream>
    #define ll long long
    #define MX 55
    using namespace std;
    int d[MX][MX];
    int x[MX],y[MX];
    int ans=0;
    int man=1<<30;
    int n;
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i){
    		scanf("%d %d",&x[i],&y[i]);
    	}
    	for(int i=1;i<=n;++i){
    		for(int j=i+1;j<=n;++j){
    			d[i][j]=d[j][i]=(abs(x[i]-x[j])+abs(y[i]-y[j]));
    		}
    	}
    	for(int i=1;i<=n;++i){
    		man=1<<30;
    		for(int j=1;j<=n;++j){
    			if(i==j) continue;
    			man=min(d[i][j],man);
    		}
    		ans=max(ans,man);
    	}
    	printf("%d",(ans+1)/2);
    	return 0;
    }
    

    这样做只有20分,为什么错了?

    因为每次取最小的会使你遗漏边,有些边是一定要选的却没选,不选的话会导致联通块不连通,举个例子吧

    如果按照上面的方法就只会选到红边,但黑边至少要选一条,这就会导致联通块不连通,所以错误了。

    正解

    一种比较巧妙的方法,可以看作在最长距离中找一个最短的曼哈顿距离便可以二分,判断联通可以用并查集或者bfs。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    int n,ans,l,r,cnt,fa[60],zx[60],zy[60];
    int find(int x){
    	if(x==fa[x]) return x;
    	else return fa[x]=find(fa[x]);
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i){
    		scanf("%d %d",&zx[i],&zy[i]);
    	}
    	l=0,r=1e9;
    	while(l<=r){
    		int mid=(l+r)>>1;
    		for(int i=1;i<=n;++i){
    			fa[i]=i;
    		}
    		for(int i=1;i<=n;++i){
    			for(int j=i+1;j<=n;++j){
    				int dis=abs(zx[i]-zx[j])+abs(zy[i]-zy[j]);
    				if(dis<=mid*2){
    					int fa1=find(i);
    					int fa2=find(j);
    					if(fa1!=fa2) fa[fa1]=fa2;
    				}
    			}
    		}
    		cnt=0;
    		for(int i=1;i<=n;++i){
    			if(fa[i]==i) cnt++;
    		}
    		if(cnt==1) r=mid-1;
    		else l=mid+1;
    	}
    	printf("%d",l);
    	return 0;
    }
    

    另外这道题还可以计算出任意两点间联通的时间,然后求最小生成树(MST),则MST的最大边就是答案。因为MST的性质之一就是满足任意两点间的最大边权最小。

    总结,今天考试总体还行,等价表达式调了好久,不知道getline怎么用。。。对于模拟还是不熟,练的太少了,代码能力不强,要多写点这种题。

  • 相关阅读:
    CART回归树基本原理(具体例子)
    Leetcode 236. 二叉树的最近公共祖先 & 235. 二叉搜索树的最近公共祖先(Python3)
    c和c++的区别
    最小编辑距离python
    判断单链表是否有环,并找出环的入口python
    关键词提取算法-TextRank
    leetcode406 ,131,1091 python
    id3算法python实现
    Relu激活函数的优点
    从 AllocateMessageQueueConsistentHash 看一致性哈希
  • 原文地址:https://www.cnblogs.com/donkey2603089141/p/11414998.html
Copyright © 2011-2022 走看看