CF#277.5div2

题目链接:codeforces.com/contest/489

A题:

思路:裸地选择排序,不过我的做法是先排序然后一直迭代输出

#include<bits/stdc++.h>
using namespace std;
struct Node{
    int v;
    int id;
    bool operator < (const Node& p)const{
        return v<p.v;
    }
}   node[3007];
bool vis[3007];
vector<int> ans;
int main(){
    int n;
    cin>>n;
    for(int i=0;i<n;++i){
        scanf("%d",&node[i].v);
        node[i].id=i;
    }
    sort(node,node+n);
    int cnt=0,idx=0,pos=0;
    memset(vis,false,sizeof(vis));
    bool flag=false;
    while(cnt<n){
        if(!flag){
            while(vis[pos]) pos++;
            idx=pos;
        }
        flag=false;
        if(!vis[node[idx].id]){
            ans.push_back(idx);
            ans.push_back(node[idx].id);
            flag=true;
        }
        vis[idx]=true;
        cnt++;
        if(flag) idx=node[idx].id;
    }
    printf("%d\n",ans.size()/2);
    for(int i=0;i<ans.size();i+=2){
        printf("%d %d\n",ans[i],ans[i+1]);
    }
    return 0;
} 



B题:

思路:第一反应是二分图的最大匹配,然后就没多想,看官方题解原来可以排序贪心做,,-_-|||,,

#include <bits/stdc++.h>
using namespace std;
bool vis[105],mp[105][105];
int mat[105],a[105],b[105];
int n,m;
bool find(int x){
    for(int y=1;y<=m;++y){
        if(!mp[x][y]) continue; 
        if(vis[y]) continue;
        vis[y]=true;
        if(mat[y]==-1||find(mat[y])){
            mat[y]=x;
            return true;
        }
    }
    return false;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;++i)
        cin>>a[i];
    cin>>m;
    for(int i=1;i<=m;++i)
        cin>>b[i];
    memset(mp,false,sizeof(mp));
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            if(abs(a[i]-b[j])<=1) mp[i][j]=true;
    int ans=0;
    memset(mat,-1,sizeof(mat));
    for(int i=1;i<=n;++i){
        memset(vis,false,sizeof(vis));
        if(find(i)) ans++;
    }
    cout<<ans<<endl;
    return 0;
}



C题:

思路:简单构造题,贪心去做,最大可能的数是尽可能让高位的数字越大,最小可能的数是先让第一位为1,然后让低位的数字尽可能的大,,

#include <bits/stdc++.h>
using namespace std;
int m,s;
int sa[105],sb[105];
void work(){
    memset(sa,0,sizeof(sa));
    memset(sb,0,sizeof(sb));
    int tmp=s-1;
    sa[1]=1;
    for(int i=m;i>=1;--i){
        if(tmp>=9){
            sa[i]+=9;
            tmp-=9;
        }
        else{
            sa[i]+=tmp;
            break;
        }
    }
    tmp=s;
    for(int i=1;i<=m;++i){
        if(tmp>=9){
            sb[i]+=9;
            tmp-=9;
        }
        else{
            sb[i]+=tmp;
            break;
        }
    }
    for(int i=1;i<=m;++i)
        printf("%d",sa[i]);
    putchar(' ');
    for(int i=1;i<=m;++i)
        printf("%d",sb[i]);
    puts("");
}
int main(){
    cin>>m>>s;
    if(s>m*9||m>1&&s<1) puts("-1 -1");
    else work();
    return 0;
}



D题:

思路:就是枚举每一个点,以该点为菱形的起点,然后标记访问到的孙子结点被访问的次数cnt,两两组合就是cnt*(cnt-1)/2个菱形,,不过我代码实现是逐个累加的,,

#include <bits/stdc++.h>
using namespace std;

int n,m,ans;
vector<int> p[3005];
int vis[3005],cnt[3005];
void dfs(int u,int root,int dep){
    if(dep==2){
        if(u==root) return ;
        if(vis[u]!=root){
            vis[u]=root;
            cnt[u]=1;
        }
        else{
            ans+=cnt[u];
            cnt[u]++;
        }
        return ;
    }
    for(int i=0;i<p[u].size();++i){
        dfs(p[u][i],root,dep+1);
    }
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;++i) p[i].clear();
    while(m--){
        int u,v;
        scanf("%d%d",&u,&v);
        p[u].push_back(v);
    }
    ans=0;
    for(int i=1;i<=n;++i){
        if(p[i].size()>=2) 
            dfs(i,i,0);
    }
    cout<<ans<<endl;
    return 0;
}



E题:

思路:看题解是分数规划做的,

r=seg( | ri - l | )/seg( bi ) 转化成  z(r) = seg( | ri - l | ) - r*seg(bi) 的一个函数,然后可以证明 z(r) 是个单调递减函数 ,所以可以用二分r,然后DP(n^2)算出dp[n]的最小花费,

当z(r) >0 ,说明还可以增大r来优化

当z(r)==0,则最优

当z(r)<0,则可以减小r来优化

DP方程:dp[i] = max{ dp[j] + sqrt(fabs(xi[i]-xi[j]-l))-r*bi[i] }


#include <bits/stdc++.h>
using namespace std;
const int N =1005;
const double EPS = 1e-9;
double dp[N];
int pre[N],sta[N];
int xi[N],bi[N];
int n,l;
// 分数规划: z(r) = seg(sqrt(ri-l)) - seg(r*bi) 
bool DP(double mid){
    dp[0]=xi[0]=bi[0]=0;
    for(int i=1;i<=n;++i){
        dp[i]=1e10;
        for(int j=0;j<i;++j){
            double tmp = dp[j]+sqrt(fabs(xi[i]-xi[j]-l)) - mid*bi[i];
            if(tmp<dp[i]){
                dp[i]=tmp;
                pre[i]=j;
            }
        }
    }
    if(dp[n]>-EPS) return true;
    else return false;
}
int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    cin>>n>>l;
    for(int i=1;i<=n;++i)
        cin>>xi[i]>>bi[i];
    double l=0,r=1e9;
    while(l+EPS<r){
        double mid=(l+r)/2;
        if(DP(mid)) l=mid;
        else r=mid;
    }
    int idx=n,top=0;
    while(idx){
        sta[top++]=idx;
        idx=pre[idx];
    }
    for(int i=top-1;i>=0;--i)
        printf("%d%c",sta[i],i==0?'\n':' ');
    return 0;
}



F题:

思路:记忆化搜索实现,先统计出每列还能放2个'1'的列数x,和每列还能放1个'1'的列数y,

   因为每行必须放两个1,所以转移方程如下:

   DP(x,y) =  DP(x-2,y+2) *x*(x-1)/2              该行从x中选两个放1,然后每列'1'为1个的数目y+2,每列'1'为2个的数目x-2

+DP(x,y-2)*y*(y-1)/2 该行从y中选两个放1,然后每列'1'为1个的数目y-2

+DP(x-1,y)*x*y 该行分别从x和y中各选一个放1,然后每列'1'为1个的数目y,每列'1'为2个的数目x-1

注意边界:x<0 or y<0 说明该构造不符合要求,返回0; x==0 and y==0 说明构造刚好符合,返回1

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 505;
LL col[N],dp[N][N];
bool vis[N][N];
LL n,m,mod;
LL DP(int x,int y){
    if(x<0||y<0) return 0;
    if(x==0&&y==0) return 1;
    if(vis[x][y]) return dp[x][y];
    LL a=(x*(x-1)/2*DP(x-2,y+2))%mod;
    LL b=(y*(y-1)/2*DP(x,y-2))%mod;
    LL c=(x*y*DP(x-1,y))%mod;
    dp[x][y]=(a+b+c)%mod;
    vis[x][y]=true;
    return dp[x][y];
}
int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin>>n>>m>>mod;
    for(int i=0;i<m;++i){
        string s;
        cin>>s;
        for(int j=0;j<n;++j)
            if(s[j]=='1') col[j]++;
    }
    int one=0,two=0;
    for(int i=0;i<n;++i){
        if(col[i]==0) two++;
        if(col[i]==1) one++;
    }
    cout<<DP(two,one)<<endl;
    //system("pause");
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值