2018年10月31日普级组

111 篇文章 0 订阅
99 篇文章 1 订阅

前言

赛后AK


VIJOS 1389 婚礼上的小杉

题目

总计N封的信件,每个信件都有自己的特征码和序号。
请按照序号递增的顺序输出信件的特征码
每行一个特征码,且特征码的格式应与输入完全一致


代码(过水)

#include <cstdio>
#include <algorithm>
#define rr register
using namespace std;
struct rec{int rk; char s[301];}a[1001]; int n;
signed cmp(rec x,rec y){return x.rk<y.rk;}
signed main(){
    rr char c=getchar();
    while (c!='\n'){
    	rr int s=0;
    	while (c<48||c>57) c=getchar();
	    while (c>47&&c<58) s=(s<<3)+(s<<1)+c-48,c=getchar();
	    a[++n].rk=s;
	}
	for (rr int i=1;i<=n;++i) scanf("%s",a[i].s+1);//完全相同就用字符串
	sort(a+1,a+1+n,cmp);//虽然时间多了,但是没关系
	for (rr int i=1;i<=n;++i) printf("%s\n",a[i].s+1);
	return 0;
}

VIJOS 1390 玩诈欺的小杉

题目

在小杉的面前有一个 N N N M M M列的棋盘,棋盘上有 N × M N\times M N×M个有黑白棋的棋子(一面为黑,一面为白),一开始都是白面朝上。可以对任意一个格子进行至多一次的操作(最多进行 N × M N\times M N×M个操作),该操作使得与该格同列的上下各2个格子以及与该格同行的左右各1个格子以及该格子本身翻面。问有多少种方法使初始状态到目标状态(不同当且仅当操作集合不同)


分析

然而可以枚举第一列,通过第一列来判断是否能回到初始状态


代码

#include <iostream>
#define rr register
using namespace std;
int n,m,t,tota,f[21];
signed main(){
	cin.tie(0); cout.tie(0); ios::sync_with_stdio(0);
	cin>>n>>m>>t;
	while (t--){
		for (rr int i=1;i<=m;++i) f[i]=0;
		for (rr int i=1;i<=n;++i){
			rr char c;
			for (rr int j=1;j<=m;++j)
			    cin>>c,f[j]|=(c-48)<<i-1;
		}
		rr int ans=0;
		for (rr int i=0;i<1<<n;++i){
			rr int now=i,old=0;
			for (rr int j=1;j<=m;++j){
				rr int temp=now;
				now=((now>>1)^(now>>2)^(now<<1)^(now<<2)^now^old^f[j])&((1<<n)-1);//上面1行,上面第2行,下面1行,下面第2行,合并前1列和该列以及目标状态列的异或值
				old=temp;
			}
			if (!now) ++ans;//恢复0
		}
		printf("%d\n",ans);
	}
	return 0;
}

VIJOS 1391 想越狱的小杉

分析

然而这道题就是单源最短路径的最短边最长问题,很容易得到松弛操作 d i s [ t o ] = m i n ( d i s [ n o w ] , w ) dis[to]=min(dis[now],w) dis[to]=min(dis[now],w)


代码

#include <cstdio>
#include <queue>
#define rr register
using namespace std;
struct node{int y,w,next;}e[400001];
int n,k,ls[2001],dis[2001],v[2001];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (c<48||c>57) c=getchar();
	while (c>47&&c<58) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
	return ans;
}
void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
signed main(){
    n=iut();
    while (1){
    	rr int x=iut(),y=iut(),w=iut();
    	if (!(x&&y&&w)) break;
    	e[++k]=(node){y,w,ls[x]}; ls[x]=k;
	}
	v[1]=1; rr queue<int>q; q.push(1); dis[1]=2147483647;
	while (q.size()){
		rr int x=q.front(); q.pop();
		for (rr int i=ls[x];i;i=e[i].next)
		if (dis[e[i].y]<min(dis[x],e[i].w)){
			dis[e[i].y]=min(dis[x],e[i].w);
			if (!v[e[i].y]) v[e[i].y]=1,q.push(e[i].y);
		}
		v[x]=0;
	}
	for (rr int i=2;i<=n;++i){
		if (!dis[i]) putchar(48);
		     else print(dis[i]);
		putchar(10);
	}
	return 0;
}

VIJOS 1392 拼拼图的小杉

题目

歹徒告诉小杉,他正在寻找的拼图块其实可以拼成 N N N个有顺序的完整的拼图。
每个完整的拼图由若干个拼图块组成。
歹徒要求小杉把拼图按拼出的顺序划分成 M M M个集合,一个拼图集合由若干个完整的拼图组成,并且总的拼图块的数目不超过 T T T。并且,构成集合的拼图是不能交叉的。
小杉要找出划分成 M M M个集合后, M M M个集合中最多能有多少个完整的拼图。


分析

那么可以发现这道题是一个动态规划的题目
对于每种拼图,那么要记录它所处的集合是哪一个及该集合的拼图数,那么可以得到 f [ i ] = m i n ( f [ j − 1 ] + a [ i ] ) ( 1 ≤ j ≤ i , 并 且 符 合 条 件 ) f[i]=min(f[j-1]+a[i])(1\leq j\leq i,并且符合条件) f[i]=min(f[j1]+a[i])(1ji,),详见代码


代码

#include <cstdio>
#define rr register
using namespace std;
int n,m,summ,a[1001];
struct rec{
	int now,sum;
	bool operator <(const rec &t)const{
		if (now!=t.now) return now<t.now;
		return sum<t.sum;
	}
	rec operator +(const int t)const{
		if (sum+t<=summ) return (rec){now,sum+t};//没有超过单个集合的限制
		    else return (rec){now+1,t};//否则要新增一个集合
	}
}f[1001];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (c<48||c>57) c=getchar();
	while (c>47&&c<58) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
	return ans;
}
signed main(){
	n=iut(); m=iut(); summ=iut(); f[0].now=1;
	for (rr int i=1;i<=n;++i) a[i]=iut();
	for (rr int i=1;i<=n;++i){
		f[i]=f[i-1]+a[i];//显而易见从上一个块递推过来
		for (rr int j=i-1;j;--j){
			rr rec t=f[j-1]+a[i];//维护最小值
			if (t<f[j]) f[j]=t;
		}
	}
	for (rr int i=n;i;--i)
	if (f[i].now<=m) return !printf("%d",i);//从后往前推
}

后续

其实纪中也有后三道题目

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值