哈尔滨理工大学软件学院大一个人赛训练Round1<二分,逆向并查集,高维DP,费用流(最大费用),瞎猜??>

只能过5题,自己还是好菜啊!新生赛的水题,知识点应该过7题的,只有那道判断5的个数不会而已,结果dp都错了,逆向并查集也想不到。

1、正向的删除可以当作逆向的添加来进行处理,同理正向的添加可以当作逆向的删除来处理。

2、找最大/最小问题,如果两次决策是有影响的就不可以轻易贪心,多考虑dp和特殊情况。

3、

wa 1 题意条件漏掉

A、A有k艘船,每艘船长度为m,有m个炮弹,这m个炮弹依次打到位置ai,问这个人该怎么摆放这些船,可以使得船最晚被打到,不被打到输出-1,相邻船的摆放要求至少隔着1的距离。

#include <iostream>
#include <stdio.h>
#include <queue>
#include <string.h>
#include <stdlib.h>
#include <set>
#include <algorithm>
#include <map>
#include <math.h>
using namespace std;
char s1[20];
char s2[5000];
int qq[5000];
int fir[205000];
int main(){
	int i,j,k,f1,f2,f3,l1,l2,f4,t1,t2,t3,t4;
    int r,c,m,n,l;
    int left1,right1,mid1;
    //freopen("in.txt","r",stdin);
    while(scanf("%d %d %d",&n,&m,&k)!=EOF){
        for(i=1;i<=n;i++)
        fir[i]=1e9+7;
        scanf("%d",&t1);
        for(i=1;i<=t1;i++){ //i时刻发射过来的
            scanf("%d",&t2);
            fir[t2]=min(fir[t2],i);
        }
        int left1,right1,mid1;
        left1=0;right1=t1; //最远能在多少时间下活着
        int max1=-10;
        while(right1>=left1){
            mid1=(left1+right1)/2;
            f1=0;f2=0,f3=m;
            for(i=1;i<=n;i++){
                if(mid1>=fir[i]){
                    f1=i;
                    f2=0;
                }else if(i-f1>=k&&f2==0){
                    f1=i;
                    f3--;
                    f2=1;
                }else if(i-f1>k&&f2==1){
                    f1=i;
                    f2=1;
                    f3--;
                }
            }
            if(f3<=0){
                left1=mid1+1;
                max1=max(max1,mid1);
            }else{
                right1=mid1-1;
            }
        }
        if(max1==t1)
        cout << "-1" <<endl;
        else
        cout << max1+1 << endl;
    }
    return 0;
}

wa 1 题意漏掉条件

B水题


一发

C 给材料A、B、C的数量分别为A1、B1、C1,AB、BC、AC这3种方法合成(每次合成2种材料各用1个)各得到不同的cost,问最大cost

范围是1e6

可以用类似折半枚举的方法,枚举合成AB的数量,此时只剩下可以合成BC和AC,那么此时的合成数量必定<=C

如果A+B>C,显然先把价值高那个先合成,如果还剩余C,就全部合成另外一种

如果A+B<C,也是显然先合成高价值的物品,剩下的C就合成低价值的物品。

如此即可。

#include <iostream>
#include <stdio.h>
#include <queue>
#include <string.h>
#include <stdlib.h>
#include <set>
#include <algorithm>
#include <map>
#include <math.h>
using namespace std;
typedef long long ll;
char s1[20];
char s2[5000];
int qq[5000];
int fir[205000];
int main(){
	ll i,j,k,f1,f2,f3,l1,l2,f4,t1,t2,t3,t4;
    ll r,c,m,n,l;
    int left1,right1,mid1;
    //freopen("in.txt","r",stdin);
    ll T;
    cin >> T;
        ll g1,g2,g3,g4,g5,g6,t5,t6;
    long long sum;
    ll min1;
    while(T--){
        cin >>t1>>t2>>t3;
        cin >>f1>>f2>>f3;
        g1=min(t1,t2);
        sum=0;
        min1=0;
        for(i=0;i<=g1;i++){
            g4=t1-i;g5=t2-i;g6=t3;
            sum=i*f1;
            if(f2>=f3){
            t4=min(g4,g6);
            g4-=t4;g6-=t4;
            sum+=t4*f2;
            t5=min(g5,g6);
            sum+=t5*f3;
            }else{
            t4=min(g5,g6);
            g5-=t4;g6-=t4;
            sum+=t4*f3;
            t5=min(g4,g6);
            sum+=t5*f2;
        }
        min1=max(sum,min1);
    }
    cout << min1 << endl;
    }
    return 0;
}

不会写原因:脑子僵硬,没想到什么可行的办法。

改善办法:多想点办法猜吧,或者根据数学的一些定义来进行推测

D给n个0和m个5,可以任意顺序排,问排出来最大能整除90的数,注意!!!0是可以算能整除90的数,同时0也是90的倍数!!

打表发现是90倍数的一个数是5555555550,9个5一个0.

然后想着90个0任意个5也是满足条件的(错误)

55555555505这个也是错误的,因为这个5不是乘出来的,而是*10+5出现的。

然后百度有这么一个套路

末1位是2或5的倍数,就能被2或5整除.
末2位是4或25的倍数,就能被4或25整除.
末3位是8或125的倍数,就能被8或125整除.
末4位是16或625的倍数,就能被16或625整除.
各位相加能被3整除,这个数能被3整除.
各位相加能被9整除,这个数能被9整除.
末3位与其它数位的差是7、11或13的倍数,就能被7、11或13整除.
末3位与其它数位的3倍的差是17或59的倍数,就能被17或59整除.
末3位与其它数位的7倍的差是19或53的倍数,就能被19或53整除.

E水题


wa了n次,明明知道dp可以做,为什么硬是想着贪心而懒得写难的方法呢,

对于前后2次有影响的操作,贪心需要慎用。

F方格取数。

给一个R*C网格表,问从左上角到右下角走2次,每次可以取走地上的数字,问走两次最多可以取得的数字是多少(格子可以重复走)

2种方法

一种是费用流

把每个格子当成一个点,对点上权值的计算可以拆点成2个点,然后容量为1,边权值为这个点的权值,然后建立个超级源点,容量为2,连接到第一个点,

然后按照每个点只能从上、或左边过来连容量为2,边权为0,再跑一边费用流即可。

注意最大费用最大流是把权值为负号加入,然后结果再加个负号。

注意!!!第i行第j列这个点是数字(i-1)*r+j而不是i*r+j。

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <queue>
#define V 1100
#define E 1000100
#define inf 99999999
using namespace std;
int vis[V]; //V为点的数量
int dist[V];
int pre[V];

struct Edge{
    int u,v,c,cost,next;
}edge[E];
int head[V],cnt;

void init(){
    cnt=0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int c,int cost)
{
    edge[cnt].u=u;edge[cnt].v=v;edge[cnt].cost=cost;
    edge[cnt].c=c;edge[cnt].next=head[u];head[u]=cnt++;

    edge[cnt].u=v;edge[cnt].v=u;edge[cnt].cost=-cost;
    edge[cnt].c=0;edge[cnt].next=head[v];head[v]=cnt++;
}

bool spfa(int begin,int end){
    int u,v;
    queue<int> q;
    for(int i=0;i<=end+2;i++){
        pre[i]=-1;
        vis[i]=0;
        dist[i]=inf;
    }
    vis[begin]=1;
    dist[begin]=0;
    q.push(begin);
    while(!q.empty()){
        u=q.front();
        q.pop();
       // cout << "find " << endl;
        vis[u]=0;
        for(int i=head[u];i!=-1;i=edge[i].next){
            if(edge[i].c>0){
                v=edge[i].v;
                if(dist[v]>dist[u]+edge[i].cost){
                    dist[v]=dist[u]+edge[i].cost;
                    pre[v]=i;
                    if(!vis[v]){
                        vis[v]=true;
                        q.push(v);
                    }
                }
            }
        }
    }
    return dist[end]!=inf;
}

int MCMF(int begin,int end){
    int ans=0,flow;
    int flow_sum=0;
    while(spfa(begin,end)){
        flow=inf;
        //cout << "一次" << endl;
        for(int i=pre[end];i!=-1;i=pre[edge[i].u])
            if(edge[i].c<flow)
                flow=edge[i].c;
        for(int i=pre[end];i!=-1;i=pre[edge[i].u]){
            edge[i].c-=flow;
            edge[i^1].c+=flow;
        }
        ans+=dist[end];
        flow_sum += flow;
    }
    //cout << flow_sum << endl;
    return ans;
}
struct ttt{
	int l,r,w;
};
ttt q1[205];
vector<int>q2;
int getid(int x){
	return lower_bound(q2.begin(),q2.end(),x)-q2.begin()+1;//返回下标+1
}
int main()
{
   // freopen("in.txt","r",stdin);
    int n,m,i,j,a,b,c,t1,t2,t3;
    int T;
    while(scanf("%d",&n)==1){
        init();m=n*n;
        addedge(0,1,2,0);
        for(i=1;i<=m;i++){
            addedge(i,i+m,2,0);
      //  cout << i << "连接到" << i+m <<"容量为1" <<"费用为0" <<endl;
        }
        for(i=1;i<=n;i++)
        for(j=1;j<=n;j++){
            if(i>1){
            addedge((i-2)*n+m+j,(i-1)*n+j,2,0);// 找上面的一个
     //   cout << n*(i-2)+m+j << "连接到" << (i-1)*n+j <<"容量为2" <<"费用为0" <<endl;
            }if(j>1){
            addedge((i-1)*n+m+j-1,(i-1)*n+j,2,0);
       // cout << n*(i-1)+m+j-1 << "连接到" << (i-1)*n+j <<"容量为2" <<"费用为0" <<endl;
            }
        }
        while(scanf("%d %d %d",&t1,&t2,&t3)==3&&(t1||t3||t2)){
            addedge((t1-1)*n+t2,(t1-1)*n+t2+m,1,-t3);
       // cout << t1*(n-1)+t2 << "连接到" << t1*(n-1)+t2+m <<"容量为1" <<"费用为"<<-t3 <<endl;
            edge[cnt-2].c=1; //满流
        }
        printf("%d\n",-MCMF(0,m+m));
    }
    return 0;
}
另外一种是高维的网格dp,

dp[i][j][k]是第一次走到i行,第二次走到j行,步数为k的时候最小值,题意就是求dp[n][n][2*n-2]的最大值。

重新写都wa一次,构造数据是找bug的最好办法,别总直接想着对拍,对拍是需要大量的时间。

i,j,k都从1开始的话,只从第一步开始加格子上的值,少加了第0步也就是1,1这个点的值

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <queue>
#define V 1100
#define E 1000100
#define inf 99999999
using namespace std;
int dp[20][20][20];
int walk[20][20];
int main(){
   // freopen("in.txt","r",stdin);
    int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4,n,m;
    while(scanf("%d",&n)==1){
        memset(dp,0,sizeof(dp));
        memset(walk,0,sizeof(walk));
        while(scanf("%d %d %d",&t1,&t2,&t3)==3&&(t1||t2||t3)){
            walk[t1][t2]=t3;
        }
        for(k=0;k<=2*n-2;k++)
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++){
                dp[i][j][k]=max(dp[i][j][k],dp[i][j][k-1]);
                dp[i][j][k]=max(dp[i][j][k],dp[i-1][j][k-1]);
                dp[i][j][k]=max(dp[i][j][k],dp[i][j-1][k-1]);
                dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-1][k-1]);
                if(i!=j){
                dp[i][j][k]+=walk[i][k+2-i]+walk[j][k+2-j];
                }else{
                dp[i][j][k]+=walk[i][k+2-i];
                }
            }
        cout << dp[n][n][n*2-2] << endl;
    }
    return 0;
}

不会写:知识点是有的,但是脑子僵硬、、、想不出

解决方法:学习呗,从这题可知,set中如果加一个结构体(有u,v),这个结构体用u排序,如果2个u相同,那么这个set加重复的u的时候并不加入(神坑神坑)

正向的删除操作可以理解为逆向的添加操作,同理,正向的添加操作可以理解为逆向的删除操作

H题

给 很多结点,每个结点有其权值,然后给边,问这个结点是否可以找到与其相连(间接/直接均可)权值比它大的点,如果有输出这个结点的下标(如果2个结点均比它大而且相同,输出下标小的那个结点的下标),如果没有输出-1

重新写也wa了好几发

wa 1 并查集写错

wa 2 没有清空qq这个数组

wa 3 判断每个集合的最大值的时候判错

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <queue>
#define V 1100
#define E 1000100
#define inf 99999999
using namespace std;
int pre[10500];
struct tt2{
    int val,num;
};
tt2 max1[10500];
int q2[10500];
void init(int x){
    for(int i=0;i<x;i++){
        pre[i]=i;
        max1[i].val=q2[i]; //max1中存的是下标,也就是这个集合的顶头所能连接到最小值的下标
        max1[i].num=i;
    }
}
int find1(int x){
    int y=x;
    while(x!=pre[x]){
        x=pre[x];
    }
    int z=y;
    while(pre[z]!=x){
        y=pre[z];
        pre[z]=x;
        z=y;
    }
    return x;
}
int find2(int x,int y){
    int x1=find1(x);
    int y1=find1(y);
    if(pre[x1]!=pre[y1]){
        if(max1[pre[x1]].val>=max1[pre[y1]].val){
            if(max1[pre[x1]].val==max1[pre[y1]].val&&
            max1[pre[x1]].num<max1[pre[y1]].num){
                max1[pre[y1]]=max1[pre[x1]];
            }else if(max1[pre[x1]].val>max1[pre[y1]].val){
                max1[pre[y1]]=max1[pre[x1]];
            }
        }
        pre[x1]=pre[y1];
    }
}
vector<int>qq[10500];
struct ttt{
    int u,v;
};
char s1[15];
ttt q5[50500];
int q6[50500];
int main(){
    //freopen("in.txt","r",stdin);
    int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4,n,m;
    int gg=0;
    while(scanf("%d",&n)==1){
        gg++;
        if(gg!=1)printf("\n");
        for(i=0;i<n;i++){
            scanf("%d",&q2[i]);
        }
       // qq.clear();
        memset(qq,0,sizeof(qq));
        init(n);
        cin >> t1;
        for(i=1;i<=t1;i++){
            scanf("%d %d",&f1,&f2);
           // cout<<f1<<"   "<<f2 <<endl;
            qq[f1].push_back(f2);
            qq[f2].push_back(f1);
        }
        cin >> t2;
        ttt u,v;
        for(i=0;i<n;i++)
            sort(qq[i].begin(),qq[i].end());
        for(i=1;i<=t2;i++){
            scanf("%s",&s1);
            if(s1[0]=='q'){
                scanf("%d",&u.u);
                u.v=-2;
                q5[i]=u;
            }else{
                scanf("%d %d",&u.u,&u.v);
                q5[i]=u;
                t3=lower_bound(qq[u.u].begin(),qq[u.u].end(),u.v)-qq[u.u].begin();
                qq[u.u].erase(qq[u.u].begin()+t3);
                t3=lower_bound(qq[u.v].begin(),qq[u.v].end(),u.u)-qq[u.v].begin();
               // cout <<"~~" <<t3<< endl;
                qq[u.v].erase(qq[u.v].begin()+t3);
            }
        }
        for(i=0;i<n;i++)
            for(j=0;j<qq[i].size();j++)
            find2(i,qq[i][j]);
        for(i=t2;i>=1;i--){
            if(q5[i].v==-2){
                f1=find1(q5[i].u);
         //       cout <<f1 <<"  "<< i<<"!!!!"<<max1[f1].val <<"     "<< q2[q5[i].u] << endl;
                if(max1[f1].val>q2[q5[i].u]){
                    q6[i]=max1[f1].num;
                }else{
                    q6[i]=-1;
                }
            }else{
                find2(q5[i].v,q5[i].u);
                q6[i]=-2;
            }
        }
        for(i=1;i<=t2;i++)
            if(q6[i]!=-2)
        printf("%d\n",q6[i]);
    }
    return 0;
}







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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值