Codeforces1287 A/B/C/D题解

题源:
A:https://vjudge.net/problem/CodeForces-1287A
B:https://vjudge.net/problem/CodeForces-1287B
C:https://vjudge.net/problem/CodeForces-1287C
D:https://vjudge.net/problem/CodeForces-1287D

A - Angry Students

队友出的 没做 懒得补 应该是水题 略了

B - Hyperset

字符串的题 意思是有n个字符串 每个字符串有m个特征 每个特征都是’S’ ‘E’ ‘T’
让你从字符串中任取三个 需要满足:这三个字符串的每个特征 要么全相同(SSS或EEE或TTT) 要么全不同(三个各取SET其中之一)
问你能找出多少组这样的字符串。(n<=1500 m<=15
自己刚开始莽啊!直接暴力 自然t在第11组了
然后又按第一个字母分组了一下 稍微优化一下 又t在第51组
最后还是没做出来。
其实自己没想到的是,如果两个字符串确定,那剩下那个字符串一定是确定的。
所以只要用map存储一下最初的字符串 最后n^2遍历所有组合可能 再看看剩下那个是否拥有即可。当然这里默认给出的字符串不重复!不然查询时不应该++ 应该加上总个数!
下面是代码:(感谢lc大佬)

#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define MID (t[k].l+t[k].r)>>1
#define cl(a,b) memset(a,b,sizeof(a))
#define dbg() printf("aaa\n")
using namespace std;
//这题思路应该是:
//首先如果两个确定 那第三个一定确定!
//所以n^2初始化当前所有种的可能性
//最后再On查询即可
//当然这题默认所给的每种子串不重复 不然还不行
int n,m;
string s[1510];
map<string,bool> mp;
char g[200][200];
void init(){
    g['S']['E']=g['E']['S']='T';
    g['S']['T']=g['T']['S']='E';
    g['E']['T']=g['T']['E']='S';
    g['S']['S']='S';
    g['E']['E']='E';
    g['T']['T']='T';
}
bool judge(int i,int j){
    string tp="";
    for(int p=0;p<m;p++){
        tp+=g[s[i][p]][s[j][p]];
    }
    return mp[tp];
}
int main() {
    ios::sync_with_stdio(false);
    init();
    cin>>n>>m;
    rep(i,1,n) {
        cin>>s[i];
        mp[s[i]]=true;
    }
    ll cnt=0;
    rep(i,1,n-1){
        rep(j,i+1,n){
            if(judge(i,j)) cnt++;
        }
    }
    cout<<cnt/3<<endl;

	return 0;
}

C - Garland

题意:给你一个序列 n个数 只有1~n这些值
有0空着 可以放剩下的数
问你最少有几个奇偶对挨着
自己思路:(贪心吧应该是
因为如果两个偶数间放个奇数或者两个奇数之间放个偶数 那显然会+2
如果在开头或末尾加上不同的数 会+1
所以优先满足两奇数或两偶数之间的0
所以排序所有偶偶区间和奇奇区间 从短到长
然后去满足
如果不够了 再满足开头和末尾
不用管奇偶区间 因为必然有一个!
自己刚开始没有考虑 特判n==1 应该输出0
加上就对了
然后先是dp算法 再是自己的方法
dp方法:
感觉很强 自己dp一直不好。。。

#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define MID (t[k].l+t[k].r)>>1
#define cl(a,b) memset(a,b,sizeof(a))
#define dbg() printf("aaa\n")
using namespace std;
//这题用dp  dp[i][j][2] 代表当前位置为i j个偶数且当前位为1奇/0偶时 最小的复杂度为多少
const int maxn=110;
int n;
int a[maxn];
int dp[maxn][maxn][2];
#define min(x,y) ((x)<(y)?(x):(y))
int main() {
	scanf("%d",&n);
	rep(i,1,n) scanf("%d",&a[i]);
	cl(dp,inf);
	dp[0][0][1]=dp[0][0][0]=0;
	rep(i,1,n){//所有长度
		rep(j,0,i/2){//当前长度 所有已有长度的可能
			if(a[i]==0||a[i]%2==0){
				dp[i][j+1][0]=min(dp[i][j+1][0],dp[i-1][j][0]);
				dp[i][j+1][0]=min(dp[i][j+1][0],dp[i-1][j][1]+1);
			}
			if(a[i]==0||a[i]%2){
				dp[i][j][1]=min(dp[i][j][1],dp[i-1][j][1]);
				dp[i][j][1]=min(dp[i][j][1],dp[i-1][j][0]+1);
			}
		}
	}
	printf("%d\n",min(dp[n][n/2][0],dp[n][n/2][1]));

	return 0;
}

下面是自己的模拟方法

#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define MID (t[k].l+t[k].r)>>1
#define cl(a,b) memset(a,b,sizeof(a))
#define dbg() printf("aaa\n")
using namespace std;
struct node{
    int l,r;
}q1[110],q0[110];
bool cmp(node a,node b){
	return (a.r-a.l)<(b.r-b.l); 
}
int main() {
	int n;
    int num[110];
	cl(num,0);
    scanf("%d",&n);
    int cnt1=0,cnt0=0;
    rep(i,1,n){
        scanf("%d",&num[i]);
        if(num[i]==0) continue;
        if(num[i]&1) cnt1++;
        else cnt0++;
    }
    if(n==1){
        printf("0\n");
        return 0;
    }
    if(cnt1==0&&cnt0==0){
    	printf("1\n");
    	return 0;
	}
    if(n&1){
    	cnt1=n/2+1-cnt1;
    	cnt0=n/2-cnt0;
	}else{
		cnt1=n/2-cnt1;
		cnt0=n/2-cnt0;
	}
    int k1=0,k0=0;//记录多少偶偶和奇奇区间了
    int s=1,t=n;
	while(num[s]==0&&s<=n) ++s;
	while(num[t]==0&&t>=1) --t;
	int x=0;//这个统计有多少不相同的 
    for(int i=s;i<t;){
        if(num[i]!=0){
            if(num[i]&1){
                for(int j=i+1;j<=t;j++){
                    if(num[j]==0) continue;
					else {
                        if(num[j]&1){
                        	if(j-i>1){
                        		k1++;
                            	q1[k1].l=i;
                            	q1[k1].r=j;
							}
                            i=j;
                            break;
                        }else {//不相同的 
                        	x++;
                        	i=j;
							break;
						}
                    }
                }
            }else{
                for(int j=i+1;j<=t;j++){
                    if(num[j]==0) continue;
					else {
                        if((num[j]&1)==0){
                        	if(j-i>1){
                        		k0++;
                            	q0[k0].l=i;
                            	q0[k0].r=j;
							}
                            i=j;
                            break;
                        }else {
                        	x++;
                        	i=j;
							break;
						}
                    }
                }
            }
        }else i++;
    }
	if(k1) sort(q1+1,q1+k1+1,cmp);
	if(k0) sort(q0+1,q0+k0+1,cmp);
	//printf("%d %d\n",cnt1,cnt0);
	int i;
	//相同奇之间 
	for(i=1;i<=k1;i++){
		if(cnt1>=q1[i].r-q1[i].l-1){
			cnt1-=(q1[i].r-q1[i].l-1);
		}else break;
	}
	//若不能填补了 
	if(i<=k1) x+=2*(k1-i+1);//每当有一组
	//再看相同偶之间 
	for(i=1;i<=k0;i++){
		if(cnt0>=q0[i].r-q0[i].l-1){
			cnt0-=(q0[i].r-q0[i].l-1);
		}else break;
	}
	if(i<=k0) x+=2*(k0-i+1);
	//看开头
	if(s!=1){
		if(num[s]&1) {
			if(cnt1>=(s-1)) cnt1-=(s-1);
			else x++;
		}else {
			if(cnt0>=(s-1)) cnt0-=(s-1);
			else x++;
		}
	}
	//看结尾 
	if(t!=n){
		if(num[t]&1){
			if(cnt1>=(n-t)) cnt1-=(n-t);
			else x++;
		}else{
			if(cnt0>=(n-t)) cnt0-=(n-t);
			else x++;
		}
	}
	printf("%d\n",x);
	
	return 0;
}

D - Numbers on Tree

题意:给出一棵有根树,每个节点都有一个权值,代表的是在其子树中有多少个节点的val比他小,现在要求根据每个点的权值构造出1~n的val数列
思路:先满足父节点 因为只有c个比他小 那他就是c+1 然后用过的数要标记一下
对于每一个子树都这样递归做
答案不唯一!
1~n一定可以表示完所有的
而且某一个结点的子树结点数量一定要大于等于这个结点的c 不然直接输出no!

#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define MID (t[k].l+t[k].r)>>1
#define cl(a,b) memset(a,b,sizeof(a))
#define dbg() printf("aaa\n")
using namespace std;
//这里可以用题解这个方法 主要原因是 深搜!
//因为搜了某一结点之后 会继续搜它的孩子结点
//所以说 a值小的 不会被兄弟的叶子节点所占领
const int maxn=2010;
int n,cnt;
struct Side{
	int v,next;
}side[maxn];
int head[maxn],c[maxn],a[maxn],num[maxn];//num是子节点
bool con[maxn];//con[i]意为 i这个值有没有被用过
void add(int u,int v){
	side[cnt].v=v;
	side[cnt].next=head[u];
	head[u]=cnt++;
}
void dfs_son(int u){//获取儿子数目
	//你瞎输出能对么!!!
	num[u]=1;//必须是1
	for(int i=head[u];i!=-1;i=side[i].next){
		int v=side[i].v;
		dfs_son(v);
		num[u]+=num[v];
	}
	return;
}
void dfs(int u){//从根节点开始嘛 因为先满足根节点
	int cnt=0;
	for(int i=1;i<=n;i++){
		if(!con[i]) cnt++;
		if(cnt==c[u]+1){
			a[u]=i;
			break;//找到当前节点的即可
		}
	}
	con[a[u]]=true;
	for(int i=head[u];i!=-1;i=side[i].next) dfs(side[i].v);
}
void init(){
	cl(head,-1);
	cl(con,0);
	cnt=0;
}
int main() {
	init();
	int root;
	scanf("%d",&n);
	rep(i,1,n){
		int f,cc;
		scanf("%d%d",&f,&cc);
		if(f!=0) add(f,i);
		else root=i;
		c[i]=cc;
	}
	dfs_son(root);
	rep(i,1,n){
		if(num[i]<c[i]+1){
			printf("NO\n");
			return 0;
		}
	}
	dfs(root);
	printf("YES\n");
	rep(i,1,n){
		printf("%d ",a[i]);
	}
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值