T1: 猫和狗
题目大意:\(k\)个观众,每个观众喜欢一条猫或狗,讨厌一条猫或狗(前猫后狗或前狗后猫),问最多满足多少个观众
1.暴力(\(25pts\))
考场分数:\(5pts\)
原因:
暴力枚举每个状态,一条猫或狗只有选和不选两种状态,\(O(2^n)\)枚举就完事
代码:
#include
#include
#include
#include
#define int long long
using namespace std;
const int maxn=500+5,INF=0x3f3f3f3f;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
return s*w;
}
int n,m,K,hate[maxn],like[maxn],black[maxn],ans,dclock,vis[maxn];
signed main(){
n=read(),m=read(),K=read();
for(int i=1;i<=K;i++){
char ch1,ch3;
int ch2,ch4;
cin>>ch1>>ch2>>ch3>>ch4;
if(ch1=='C'){
if(!vis[ch2])vis[ch2]=++dclock;
if(!vis[ch4+n])vis[ch4+n]=++dclock;
like[i]=vis[ch2],hate[i]=vis[ch4+n];
}
if(ch1=='D'){
if(!vis[ch2+n])vis[ch2+n]=++dclock;
if(!vis[ch4])vis[ch4]=++dclock;
like[i]=vis[ch2+n],hate[i]=vis[ch4];
}
}
long long maxs=(1<
2.二分图(\(100pts\))
万万想不到,\(T1\)竟然是二分图原题(然而最后放在二分图的练习的最后一道,压根就没做)
由于一个观众,喜欢猫就讨厌狗,喜欢狗就讨厌猫
所以可以逆向思维,用二分图把冲突的情况处理掉
把喜欢这个动物的与讨厌这个动物的相连,跑二分图,最后答案为:\(n-tot/2\)(有\(tot\)个冲突,说明只能满足一半)
#include
#include
#include
#include
#include
using namespace std;
const int maxn=500+5,INF=0x3f3f3f3f;
inline int read(){
int s=0,w=1;咕咕
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
return s*w;
}
int n,m,K,ans,a[maxn][maxn],girls[maxn],vis[maxn];
char s1[maxn][maxn],s2[maxn][maxn];
vector g[maxn];
bool Find(int u){
for(int i=0;i>s1[i]>>s2[i];
}
for(int i=1;i<=K;i++){
for(int j=1;j<=K;j++){
if(!strcmp(s1[i],s2[j]))g[i].push_back(j),g[j].push_back(i);
t}
}
for(int i=1;i<=K;i++){
memset(vis,0,sizeof(vis));
if(Find(i))ans++;
}
cout<
T2:旋转子段
题目大意:翻转某个子段,使得翻转后整个序列中满足\(a[i]==i\)的个数最多
1.\(O(n^3)\) 枚举(\(35pts\))
三维,第一维枚举区间长度,第二维枚举左右端点,第三维扫区间,柿子:\(ans=max(sum[l-1]+sum[n]-sum[r]+num)\) (\(num\)为区间内满足条件个数)
#include
#include
#include
#include
using namespace std;
const int maxn=5000+5,INF=0x3f3f3f3f;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
return s*w;
}
int sum[maxn],n,f[maxn],a[maxn],ans;
int main(){
n=read();
for(int i=1;i<=n;i++){
a[i]=read();
sum[i]=sum[i-1];
if(a[i]==i)sum[i]++;
}
ans=sum[n];
if(sum[n]==n)return cout<
2.\(O(n^2)\)枚举(\(65pts\))
这里已经用了一个特殊性质了:\(l=min(a[i],i)\) \(r=max(a[i],i)\)
因此三维枚举可以直接转化为:一维枚举中点,第二维左右扩展
#include
#include
#include
#include
using namespace std;
const int maxn=5e5+50;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
return s*w;
}
int n,a[maxn],sum[maxn],ans,l,r,cnt;
int main(){
n=read();
for(register int i=1;i<=n;i++){
a[i]=read();
sum[i]=sum[i-1];
if(a[i]==i)sum[i]++;
}
for(register int i=1;i<=n;i++){
x=a[i];
l=min(x,i),r=max(i,x);
if(l==r)continue;
cnt=0;
while(l<=r){
if(l!=r){
if(a[l]==r)cnt++;
if(a[r]==l)cnt++;
}else if(l==r){
if(a[l]==l)cnt++;
}
l++,r--;
}
ans=max(ans,cnt+sum[min(x,i)]+sum[n]-sum[max(x,i)]);
}
cout<
3.桶+\(STL\)(\(100pts\))
再细细观察,又能得到第二个性质:如果存在\(a[i]+i==a[j]+j\),那么以\(\frac{i+j}{2}\)为中点,且翻转区间包含\(i\)和\(j\),翻转后\(i\)和\(j\)都能满足条件
证明略了,干瞪眼观察得到
因此,可以开个桶维护一下\(a[i]+i\),然后枚举每一个\(a[i]+i\),由之前的性质可得到左端点和右端点,然后像前两个一样搞它就完事了
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=1e6+5,INF=0x3f3f3f3f;
int n,sum[maxn],a[maxn],ans;
vector ve[maxn];
bool cmp(int x,int y){return abs(2*x-a[x]-x)>n;
for(int i=1;i<=n;i++){
cin>>a[i];
sum[i]=sum[i-1];
if(a[i]==i)sum[i]++;
ve[a[i]+i].push_back(i);
}
for(int i=2;i<=2*n;i++){
if(!ve[i].empty()){
sort(ve[i].begin(),ve[i].end(),cmp);
for(int j=0;jr)swap(l,r);
ans=max(ans,sum[l-1]+sum[n]-sum[r]+j+1);
}
}
}
cout<
3.走格子
题目大意:一个矩阵,一堆能走的点,期间能在墙上开传送门,然后求从起点到终点的最短路线
感觉是考试中较简单的一道题了,然而打的正解挂了\(75pts\),原因是没注意必须要在墙上开门,以为可以原地开门(其实就是没考虑全,毕竟很多时候要开传送门的格子旁边就是墙)
乱搞就完事了,一个格子对上下左右能走的地方连边
然后考虑传送门,由于传送门只能由墙边传到墙边,所以可以直接从当前格子往上下左右走,看最后停的位置,然后连边,边权为上下左右中最短的距离\(+1\),因为必须要走到墙边才能打传送门,如图
代码:
#include
#include
#include
#include
#include
using namespace std;
const int maxn=1000+5,maxe=6e6+5,INF=0x3f3f3f3f;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
return s*w;
}
int n,m,ans=INF,tx,vis[maxe],ty,qx,qy,head[maxe],tot,dis[maxe],inq[maxe];
char a[maxn][maxn];
struct Edge{
int to,next,from,dis;
}e[maxe];
void Add(int x,int y,int z){
e[++tot].next=head[x];
e[tot].to=y;
e[tot].from=x;
e[tot].dis=z;
head[x]=tot;
}
void Spfa(int S){
for(int i=0;i<=n*m*2;i++)dis[i]=INF;
dis[S]=0;
queue q;q.push(S);
while(!q.empty()){
int u=q.front();q.pop();vis[u]=0;//cout<dis[u]+e[x].dis){
dis[v]=dis[u]+e[x].dis;
if(!vis[v]){
// if(++inq[v]>n*m)break;
q.push(v);vis[v]=1;
}
}
}
}
if(dis[(tx-1)*m+ty]==INF)puts("no");
else cout<>a[i][j];
if(a[i][j]=='C')qx=i,qy=j,a[i][j]='.';
if(a[i][j]=='F')tx=i,ty=j,a[i][j]='.';
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]=='.'){
if(a[i+1][j]=='.')Add((i-1)*m+j,i*m+j,1);
if(a[i-1][j]=='.')Add((i-1)*m+j,(i-2)*m+j,1);
if(a[i][j+1]=='.')Add((i-1)*m+j,(i-1)*m+j+1,1);
if(a[i][j-1]=='.')Add((i-1)*m+j,(i-1)*m+j-1,1);
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]=='.'){
int x1=i,x2=i,y1=j,y2=j,diss=INF;
while(a[x1+1][j]=='.')x1++;
while(a[i][y1+1]=='.')y1++;
while(a[x2-1][j]=='.')x2--;
while(a[i][y2-1]=='.')y2--;
diss=min(min(x1-i+1,i-x2+1),min(y1-j+1,j-y2+1));
Add((i-1)*m+j,(x1-1)*m+j,diss);
Add((i-1)*m+j,(x2-1)*m+j,diss);
Add((i-1)*m+j,(i-1)*m+y1,diss);
Add((i-1)*m+j,(i-1)*m+y2,diss);
}
}
}
Spfa((qx-1)*m+qy);
return 0;
}
T4:柱状图
模拟退火,\(O(1)\)随机搞最大高度,\(O(n)\)枚举最高点坐标
口古口古口古