20180922北京网络赛题解
20180922北京网络赛题解
A.Saving Tang Monk II
MEANING
\(n \times m\)的网格图,可以向四个方向移动,移动一格耗时1秒,毒气室额外花费1秒,S
表示起点,T
表示终点,#
表示毒气室,拥有氧气瓶才能进入,且消耗一个氧气瓶,每次进入B
获得一个氧气瓶,最多同时携带5瓶氧气瓶,进入P
获得一个加速,使消耗的时间减少1秒。.
表示空的房间。问从S到T最少耗时。
SOLUTION
优先队列BFS,耗时少的点优先。vis[x][y][k]表示在(x,y)有k个氧气瓶,每个状态只访问一次。
CODE
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 105;
const int dirx[] = {1,-1,0,0};
const int diry[] = {0,0,1,-1};
char mapp[MAXN][MAXN];
bool vis[MAXN][MAXN][6];
int n,m;
struct node{
int x,y,step,oxg;
};
bool operator<(node a,node b){
return a.step>b.step;
}
int bfs(node st,node et){
priority_queue<node> q;
memset(vis,0,sizeof vis);
q.push(st);
vis[st.x][st.y][st.oxg] = 1;
while(q.size()){
node cur = q.top();
if(cur.x==et.x&&cur.y==et.y)return cur.step;
q.pop();
for(int i=0;i<4;i++){
int xx = cur.x+dirx[i],yy = cur.y+diry[i],oo = cur.oxg,ss = cur.step;
if(xx<0||xx>=n||yy<0||yy>=m)continue;
if(oo==0&&mapp[xx][yy]=='#')continue;
if(mapp[xx][yy]=='#')oo--,ss++;
if(mapp[xx][yy]=='B')oo = min(oo+1,5);
if(vis[xx][yy][oo])continue;
vis[xx][yy][oo] = 1;
if(mapp[xx][yy]=='P')q.push({xx,yy,ss,oo});
else q.push({xx,yy,ss+1,oo});
}
}
return -1;
}
int main() {
while(scanf("%d%d",&n,&m)&&n){
for(int i=0;i<n;i++){
scanf("%s",mapp[i]);
}
node st,et;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(mapp[i][j]=='S'){
st={i,j,0,0};
}
if(mapp[i][j]=='T'){
et={i,j,0,0};
}
}
}
cout<<bfs(st,et)<<endl;
}
return 0;
}
B.Tomb Raider
MEANING
n个字符串,每个字符串是个环,可以自己选起点,输出字典序最小的LCS。
CODE
数据很小,暴力
队友代码
#include <stdio.h>
#include <time.h>
#include <algorithm>
#include <cstring>
#include <stack>
#include <queue>
#include <string>
#include <iostream>
#include <set>
using namespace std;
typedef long long ll;
const int maxn = 2000005;
struct SSAM {
int next[50][26];
int len;
set<int>cp[26];
int find(int p,int ch){
set<int>::iterator it = cp[ch].upper_bound(p);
if (it==cp[ch].end()) return -1;
else return *it;
}
void build(char s[]){
for (int i=0;i<26;i++) cp[i].clear();
len = strlen(s);
for (int i=0;i<len;i++){
cp[s[i]-'a'].insert(i+1);
cp[s[i]-'a'].insert(i+len+1);
}
for (int i=0;i<=2*len;i++){
for (int j=0;j<26;j++){
next[i][j] = find(i,j);
}
}
}
bool check(char s[]){
int le = strlen(s);
for (int i=0;i<len;i++){
int j = i;
for (int k=0; k<le;k++ ) {
j = next[j][s[k]-'a'];
if ( j==-1 || j> i+len ) break;
// printf("j=%d\n",j);
}
if ( j!=-1 && j <= i+len) return true;
}
return false;
}
void show(){
for (int i=0;i<=2*len;i++){
for (int j=0;j<26;j++){
printf("%d ",next[i][j]);
}printf("\n");
}
printf("\n");
}
}ssam[20];
ll n;
char str[50][20];
char tmp[50];
string solve(){
int len = strlen(str[1]);
string ans = "";
for (int i=1;i<=(1<<len)-1;i++){
int k=0;
for (int j=0;j<len;j++) {
if (i & (1 << j)) tmp[k++] = str[1][j];
}
tmp[k]='\0';
for (int qwe=0;qwe<k;qwe++) {
int ff = 1;
for (int l = 2; l <= n; l++) {
if (!ssam[l].check(tmp)) {
ff = 0;
break;
}
}
if (ff == 1) {
string tt = tmp;
if (tt.length() > ans.length()) {
ans = tt;
} else if (tt.length() == ans.length()) {
if (tt < ans) {
ans = tt;
}
}
}
char rty = tmp[0];
for (int asd=1;asd<k;asd++) tmp[asd-1] = tmp[asd];
tmp[k-1] = rty;
}
}
return ans;
}
int main() {
while (scanf("%lld", &n) != EOF) {
for (int i = 1; i <= n; i++) {
scanf("%s", str[i]);
ssam[i].build(str[i]);
}
string ans = solve();
if (ans == "") {
printf("0\n");
} else {
printf("%s\n", ans.c_str());
}
}
return 0;
}
C.Cheat
MEANING
四个人打一副牌,先出完所有牌的player获胜。游戏的规则是,四个人按顺时针轮流出牌,每次出牌时将牌反置,并声明自己所出的牌均为某一点数(RANK)。其余三人依次决定是否质疑。如果选择质疑,翻开牌,如果与声明一致,质疑者将牌桌所有牌收回手中,反之出牌者将所有牌收回手中。如果没人质疑,牌将留在桌上。每个人声明的RANK从A开始,后一人声明的RANK必须且只能比前一人声明的大一级,特别的,K之后为A,如此循环。
这四个人每个都有自己的出牌策略和质疑策略。
player1:出牌策略:如果拥有当前必须声明的RANK,打出一张。否则不得不lie,将一张手中RANK的字典序最小的牌打出,并声明为当前RANK。质疑策略:当自己是下一个出牌者,且不得不lie时,质疑之。或者当前出牌者声明的RANK有p张牌,自己手中有q张时,且p+q>4,质疑之。
player2:出牌策略:如果拥有当前必须声明的RANK,打出该RANK的全部牌,否则不得不lie,将一张手中RANK的字典序最小的牌打出,并声明为当前RANK。质疑策略:当且仅当自己是下一个出牌者,且不得不lie时,质疑之。
player3:出牌策略:如果拥有当前必须声明的RANK,打出该RANK的全部牌,否则不得不lie,打出数量最小的某一RANK的全部牌,如果有多种,打出RANK的字典序最小的,并声明为当前RANK。质疑策略:当且仅当自己拥有出牌者所声明的RANK的全部4张牌时,质疑之。
player4:出牌策略:如果拥有当前必须声明的RANK且有3张或者4张时,打出该RANK的全部牌。否则除了打出该RANK的全部牌(如果有的话),额外加上一张RANK的字典序最小的牌(如果有的话)。质疑策略:当且仅当出牌者没有手牌时,质疑之。
SOLUTION
按题意模拟。
有两个坑。
一是牌的RANK字典序和RANK不一样,容易忽略J<K<Q。
二是player4需要出的牌少于三张时,而又无其它RANK的牌时,此时说的是真话。
CODE
#include<bits/stdc++.h>
using namespace std;
int Rank[] = {9, 1, 2, 3, 4, 5, 6, 7, 8, 0, 10, 12, 11}; //rank
int hand[5][14];//lex
int tran(char s[]) { // char[] to number
if(strcmp(s, "A") == 0)return 9;
if(strcmp(s, "J") == 0)return 10;
if(strcmp(s, "Q") == 0)return 12;
if(strcmp(s, "K") == 0)return 11;
if(strcmp(s, "10") == 0)return 0;
return s[0] - '0' - 1;
}
void NeTran(int num,char s[]){
if(num==9)strcpy(s,"A");
else if(num==10)strcpy(s,"J");
else if(num==12)strcpy(s,"Q");
else if(num==11)strcpy(s,"K");
else if(num==0)strcpy(s,"10");
else{
s[0] = num+1+'0';
s[1] = '\0';
}
}
bool check(int player) {
for(int i = 0; i < 13; i++)if(hand[player][i])return false;
return true;
}
int cnt;
int roundHolder, requireRank;
int cardNum;
bool state;
int realcard[15];
void putDown(int player) {
memset(realcard,0,sizeof realcard);
cardNum = 0;
if(player == 1) {
if(hand[player][requireRank]) {
hand[player][requireRank]--;
realcard[requireRank]++;
cardNum = 1;
state = 1;
} else {
for(int i = 0; i < 13; i++) {
if(hand[player][i]) {
hand[player][i]--;
realcard[i]++;
cardNum = 1;
break;
}
}
state = 0;
}
}
else if(player == 2) {
if(hand[player][requireRank]) {
realcard[requireRank] = hand[player][requireRank];
cardNum = hand[player][requireRank];
hand[player][requireRank] = 0;
state = 1;
} else {
for(int i = 0; i < 13; i++) {
if(hand[player][i]) {
hand[player][i]--;
realcard[i]++;
cardNum = 1;
break;
}
}
state = 0;
}
}
else if(player == 3) {
if(hand[player][requireRank]) {
realcard[requireRank] = hand[player][requireRank];
cardNum = hand[player][requireRank];
hand[player][requireRank] = 0;
state = 1;
} else {
int tot = -1;
for(int i = 0; i < 13; i++) {
if(hand[player][i] && (tot==-1||hand[player][i]<hand[player][tot]))tot = i;
}
realcard[tot] = hand[player][tot];
cardNum = hand[player][tot];
hand[player][tot] = 0;
state = 0;
}
}
else {
if(hand[player][requireRank] == 3 || hand[player][requireRank] == 4) {
realcard[requireRank] = hand[player][requireRank];
cardNum = hand[player][requireRank];
hand[player][requireRank] = 0;
state = 1;
} else {
realcard[requireRank] = hand[player][requireRank];
cardNum = hand[player][requireRank];
hand[player][requireRank] = 0;
state = 1;
for(int i = 0; i < 13; i++) {
if(hand[player][i]) {
hand[player][i]--;
realcard[i]++;
cardNum++;
state = 0;
break;
}
}
}
}
}
int nextRequireRank() {
return Rank[(cnt + 1) % 13];
}
bool willChallenge(int player) {
if(player == 1) {
if(roundHolder == 4 && hand[player][nextRequireRank()]==0)
return true;
for(int i = 0; i < 13; i++) {
if(hand[player][requireRank] + cardNum > 4)return true;
}
return false;
}
else if(player == 2) {
if(roundHolder == 1 && hand[player][nextRequireRank()]==0)
return true;
return false;
}
else if(player == 3) {
if(hand[player][requireRank] == 4)return true;
return false;
}
else{
if(check(roundHolder))return true;
return false;
}
}
bool challenge() {
return !state;
}
int table[15];
void takesBack(int player) {
for(int i = 0; i < 13; i++) {
hand[player][i] += table[i];
table[i] = 0;
}
}
void init() {
memset(table,0,sizeof table);
memset(hand,0,sizeof hand);
memset(realcard,0,sizeof realcard);
}
int main() {
// IN_PC();
char card[5];
while(scanf("%s", card) != EOF) {
init();
hand[1][tran(card)]++;
for(int i = 0; i < 12; i++) {
scanf("%s", card);
hand[1][tran(card)]++;
}
for(int i = 2; i <= 4; i++) {
for(int j = 0; j < 13; j++) {
scanf("%s", card);
hand[i][tran(card)]++;
}
}
cnt = 0;
roundHolder = 1, requireRank = Rank[cnt % 13];
int winner = 0;
while(1) {
putDown(roundHolder);
for(int i = 0; i < 13; i++) {
table[i] += realcard[i];
}
for(int i = 1; i <= 3; i++) {
int challenger = (roundHolder % 4 + i-1)%4+1;
if(willChallenge(challenger)) {
if(challenge())
takesBack(roundHolder);
else
takesBack(challenger);
}
}
if(check(roundHolder)) {
winner = roundHolder;
break;
}
cnt++;
requireRank = Rank[cnt % 13];
roundHolder = roundHolder % 4 + 1;
}
for(int i = 1; i <= 4; i++) {
if(winner==i){
printf("WINNER\n");
}
else{
int flag = 0;
for(int j=0;j<13;j++){
for(int k=0;k<hand[i][Rank[j]];k++){
char ans[5];
NeTran(Rank[j],ans);
if(flag==0){
flag = 1;
}
else printf(" ");
printf("%s",ans);
}
}
puts("");
}
}
}
return 0;
}
D.80 Days
CODE
队友代码
#include <stdio.h>
#include <time.h>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2000005;
ll val[maxn<<2];
ll sum[maxn];
void build(ll rt,ll l,ll r){
if (l==r){
val[rt] = sum[l];
} else {
ll mid = (l+r)/2;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
val[rt] = min(val[rt<<1],val[rt<<1|1]);
}
}
ll query(ll rt,ll l,ll r,ll L,ll R){
if (L<=l&&r<=R){
return val[rt];
} else {
ll mid = (l+r)/2;
ll ret = 1e18;
if (L<=mid) ret = min(ret,query(rt<<1,l,mid,L,R));
if (mid<R) ret =min(ret,query(rt<<1|1,mid+1,r,L,R));
return ret;
}
}
int main() {
ll t;
scanf("%lld",&t);
while (t--){
ll n,c;
scanf("%lld%lld",&n,&c);
for (int i=1;i<=n;i++) {
scanf("%lld",&sum[i]);
}
for (int i=1;i<=n;i++) {
ll tmp;
scanf("%lld",&tmp);
sum[i]-=tmp;
sum[n+i]=sum[i];
}
for (ll i=1;i<=2*n;i++){
sum[i]=sum[i-1]+sum[i];
}
build(1,1,2*n);
ll ans = -1;
for (int i=1;i<=n;i++){
if ( (sum[i]-sum[i-1]+c>=0)&& query(1,1,2*n,i,n+i-1) + c >= sum[i-1] ){
ans = i;
break;
}
}
printf("%lld\n",ans);
}
return 0;
}