[题解]CF Round #385 (Div.2)

745A:Hongcow Learns the Cyclic Shift

题意简述

给出一个字符串 S
可以将字符串循环位移,即将首位移到最后。
问这样操作若干次最多形成多少本质不同的字符串。

数据范围

1|S|50

思路

将原串复制后接到后面。
把所有的字符串扔到一个set里。
最后set的大小即为答案。

代码

#include<cstdio>
#include<cstring>
#include<set>
#include<string>
using namespace std;
set<string> se;
char st[200];
int len;
int main()
{
    scanf("%s",st);
    len=strlen(st);
    for (int i=0;i<len;i++)
        st[len+i]=st[i];
    for (int i=0;i<len;i++)
    {
        string hh(st+i,st+i+len);
        se.insert(hh);
    }
    printf("%d",se.size());
    return 0;
}

745B:Hongcow Solves A Puzzle

题意简述

给你一个 nm 的矩阵。
#所形成的四连通块表示这个拼图版的形状。
你有两个完全相同的拼图版。
不能旋转和翻转,只允许平移。
问这两个完全相同的拼图版能不能拼成一个矩形。

数据范围

1n,m50

思路

只有给出的形状是矩形才满足题意。

代码

#include<cstdio>
using namespace std;
char read()
{
    char ch=getchar();
    while (ch!='X'&&ch!='.')
        ch=getchar();
    return ch;
}
int n,m;
bool visx[1000],visy[1000];
char ma[1000][1000];
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
        {
            ma[i][j]=read();
            if (ma[i][j]=='X')
                visx[i]=1,visy[j]=1;
        }
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            if (visx[i]&&visy[j]&&ma[i][j]=='.')
            {
                printf("NO");
                return 0;
            }
    printf("YES");
    return 0;
}

745C:Hongcow Builds A Nation

题意简述

给出 n 个节点m条边的无向图。
其中有 k 个节点是关键点。
保证这k个点不连通。
要求添加尽量多的边,不能出现重边和自环。使得 k 个点依旧不连通。
问最多添加多少边。

数据范围

1kn1000
1m100000

思路

每个连通块的边数是 C2n
dfs搞出来所有的连通块。
对于没有关键点的连通块,把它加入到点数最多的有关键点的连通块。
最后减掉 m 就是答案。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct edge{
    int s,t,next;
}e[200010];
int head[100010],cnt;
void addedge(int s,int t)
{
    e[cnt].s=s;e[cnt].t=t;e[cnt].next=head[s];head[s]=cnt++;
    e[cnt].s=t;e[cnt].t=s;e[cnt].next=head[t];head[t]=cnt++;
}
int n,m,k,u,v,ans;
int spe[1010],num[1010];
bool vis[1010];
int dfs(int node)
{
    vis[node]=1;
    int ret=1;
    for (int i=head[node];i!=-1;i=e[i].next)
        if (!vis[e[i].t])
            ret+=dfs(e[i].t);
    return ret;
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for (int i=1;i<=k;i++)
        scanf("%d",&spe[i]);
    memset(head,0xff,sizeof(head));
    cnt=0;
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        addedge(u,v); 
    }
    for (int i=1;i<=k;i++)
        num[i]=dfs(spe[i]);
    sort(num+1,num+k+1);
    for (int i=1;i<=n;i++)
        if (!vis[i])
            num[k]++;
    for (int i=1;i<=k;i++)
        ans+=num[i]*(num[i]-1)/2;
    ans-=m;
    printf("%d",ans);
    return 0;
}

745D:Hongcow’s Game

题意简述

本题为交互题。
有一个nn的矩阵 a
0ai,j109
其中 ai,i=0
你被要求求出每一行, min(ai,j),ji 的最小值。
即每一行除去对角线位置上的数的最小值。
允许最多询问 20 次,每次询问你必须给出一个下标集合 w1,w2,...,wk(kn)
会给你返回 n 个数,pi表示 min(i,wj),1jk

数据范围

1n1000

思路

题目本质是构造出若干个集合,使得这些集合取并集,能够得出所有的 {x|1xn}{j},1jn
考虑分治。
构造出这样的一个 66 的矩形

X22
X22
33X22
111X3
111X
1113

我们可以通过 1,2 两次操作把这个问题划归成两个范围为 n2 的子问题,并且这两个问题是互不影响的。
我们可以通过类似的方式。继续向下递归。
同一层的互不影响的子问题可以合并,如图中的 3
这样的次数是2log2n的。
满足要求。

代码

#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
vector<int> f[30];
int n,cnt,tmp;
int ans[1010],hh[1010];
bool vis[1010];
void dfs(int l,int r,int de)
{
    if (l==r)
        return;
    int mid=(l+r)>>1;
    for (int i=l;i<=mid;i++)
        f[de*2-1].push_back(i);
    dfs(l,mid,de+1);
    for (int i=mid+1;i<=r;i++)
        f[de*2].push_back(i);
    dfs(mid+1,r,de+1);
    cnt=max(cnt,de*2);
}
int main()
{
    scanf("%d",&n);
    dfs(1,n,1);
    memset(ans,0x3f,sizeof(ans));
    for (int i=1;i<=cnt;i++)
    {
        tmp=f[i].size();
        printf("%d\n",tmp);
        for (int j=0;j<tmp;j++)
            printf("%d%c",f[i][j]," \n"[j==(tmp-1)]);
        fflush(stdout);
        for (int j=1;j<=n;j++)
            scanf("%d",&hh[j]);
        memset(vis,0,sizeof(vis));
        for (int j=0;j<tmp;j++)
            vis[f[i][j]]=1;
        for (int j=1;j<=n;j++)
            if (!vis[j])
                ans[j]=min(ans[j],hh[j]);
    }
    printf("-1\n");
    for (int i=1;i<=n;i++)
        printf("%d%c",ans[i]," \n"[i==n]);  
    return 0;
} 

745E:Hongcow Buys a Deck of Cards

题意简述

你要购买 n 张卡片。卡片有红色和蓝色两种颜色。
每张卡片的标价为(ri,bi)表示需要为 ri 枚红色货币和 bi 枚蓝色货币。
实际购买的时候的费用是 (max(riA,0),max(biB,0)) 其中 A 表示你已经持有红色卡片,B表示你已经持有的蓝色卡片。
货币会因为购买而被消耗,而卡片只减费而不会被消耗。
每回合你可以进行两种操作:
1.攒 1 枚红色货币和1枚蓝色货币。
2.购买某张卡片。
问至少需要几轮,你可以买到所有卡片。

数据范围

1n16
1ri,bi107

思路

状压DP。
因为我们最后的费用只跟我们减的费有关系,考虑将减的费作为状态进行DP。
因为一共只有16张卡片,所以最多被减的费用为 15i=1i=120
f[mask][j] 表示购买的卡片为 mask ,红色货币一共省了 j 枚时,最多省蓝色货币多少。
计算一下每一个没有选的卡片,每种货币能省多少,向后转移。
因为省的费用确定,所以这样比较方便。
时间复杂度O(2nn2)

代码

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define INF 0x3f3f3f3f
char read()
{
    char ch=getchar();
    if (ch!='R'&&ch!='B')
        ch=getchar();
    return ch;
}
int n,lim,sumr,sumb,ans;
char opt[20];
int ndr[20],ndb[20],saver[20],saveb[20];
int r,b,f[66000][150];
int main()
{
    scanf("%d",&n);
    lim=(1<<n)-1;
    for (int i=0;i<n;i++)
    {
        opt[i]=read();
        scanf("%d%d",&ndr[i],&ndb[i]);
        sumr+=ndr[i];
        sumb+=ndb[i];
    }
    memset(f,0xef,sizeof(f));
    f[0][0]=0;
    for (int i=0;i<=lim;i++)
    {
        r=0,b=0;
        for (int j=0;j<n;j++)
            if (i&(1<<j))
            {
                r+=(opt[j]=='R');
                b+=(opt[j]=='B');
            }
        for (int j=0;j<n;j++)
        {
            saver[j]=min(ndr[j],r);
            saveb[j]=min(ndb[j],b);
        }
        for (int j=0;j<n;j++)
            if (!(i&(1<<j)))
                for (int k=0;k<=130;k++)
                    f[i|(1<<j)][k+saver[j]]=max(f[i|(1<<j)][k+saver[j]],f[i][k]+saveb[j]);
    }
    ans=INF;
    for (int i=0;i<=130;i++)
        ans=min(ans,max(sumr-i,sumb-f[lim][i]));
    printf("%d",ans+n);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值