745A:Hongcow Learns the Cyclic Shift
题意简述
给出一个字符串
S
。
可以将字符串循环位移,即将首位移到最后。
问这样操作若干次最多形成多少本质不同的字符串。
数据范围
思路
将原串复制后接到后面。
把所有的字符串扔到一个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
题意简述
给你一个
n∗m
的矩阵。
#
所形成的四连通块表示这个拼图版的形状。
你有两个完全相同的拼图版。
不能旋转和翻转,只允许平移。
问这两个完全相同的拼图版能不能拼成一个矩形。
数据范围
1≤n,m≤50
思路
只有给出的形状是矩形才满足题意。
代码
#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
个节点
其中有
k
个节点是关键点。
保证这
要求添加尽量多的边,不能出现重边和自环。使得
k
个点依旧不连通。
问最多添加多少边。
数据范围
1≤m≤100000
思路
每个连通块的边数是
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
题意简述
本题为交互题。
有一个
其中
ai,i=0
你被要求求出每一行,
min(ai,j),j≠i
的最小值。
即每一行除去对角线位置上的数的最小值。
允许最多询问
20
次,每次询问你必须给出一个下标集合
w1,w2,...,wk(k≤n)
会给你返回
n
个数,
数据范围
1≤n≤1000
思路
题目本质是构造出若干个集合,使得这些集合取并集,能够得出所有的
{x|1≤x≤n}−{j},1≤j≤n
考虑分治。
构造出这样的一个
6∗6
的矩形
X | 2 | 2 | ||
X | 2 | 2 | ||
3 | 3 | X | 2 | 2 |
1 | 1 | 1 | X | 3 |
1 | 1 | 1 | X | |
1 | 1 | 1 | 3 |
我们可以通过
1,2
两次操作把这个问题划归成两个范围为
n2
的子问题,并且这两个问题是互不影响的。
我们可以通过类似的方式。继续向下递归。
同一层的互不影响的子问题可以合并,如图中的
3
。
这样的次数是
满足要求。
代码
#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
张卡片。卡片有红色和蓝色两种颜色。
每张卡片的标价为
实际购买的时候的费用是
(max(ri−A,0),max(bi−B,0))
其中
A
表示你已经持有红色卡片,
货币会因为购买而被消耗,而卡片只减费而不会被消耗。
每回合你可以进行两种操作:
1.攒
1
枚红色货币和
2.购买某张卡片。
问至少需要几轮,你可以买到所有卡片。
数据范围
1≤n≤16
1≤ri,bi≤107
思路
状压DP。
因为我们最后的费用只跟我们减的费有关系,考虑将减的费作为状态进行DP。
因为一共只有16张卡片,所以最多被减的费用为
∑15i=1i=120
f[mask][j]
表示购买的卡片为
mask
,红色货币一共省了
j
枚时,最多省蓝色货币多少。
计算一下每一个没有选的卡片,每种货币能省多少,向后转移。
因为省的费用确定,所以这样比较方便。
时间复杂度
代码
#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;
}