2018 ICPC Asia Jakarta Regional Contest
A Edit Distance
题目链接
题意:对于一个字符串S,可以选择插入一个字母、删除一个字母、替换一个字母,将字符串S通过以上步骤变换为字符串T,其最小步骤数为edit(S,T)。求对于字符串S,编辑为相同长度的字符串T且满足edit(S,T)要大于S的长度除于2。
思路:考虑将01字符串中满足大于等于1半的字符翻转。最差情况字符串edit(S,T)==|S|/2。即0字符和1字符个数相同,根据首字符元素,选择另一个字符翻转,再将首字符翻转。满足题目要求条件。
翻转首字符意味不能通过头尾拼接得到。
#include<bits/stdc++.h> using namespace std; const int maxn = 1e5 + 3; int a[maxn]; char s[maxn]; int main() { scanf("%s", s); int len = strlen(s); int num[2] = { 0 }; for (int i = 0; i < len; i++) { num[s[i] - '0']++; } if (num[0] < num[1]) { for (int i = 0; i < len; i++) { if (s[i] == '1') { s[i] = '0'; } } } else if(num[0] > num[1]) { for (int i = 0; i < len; i++) { if (s[i] != '1') { s[i] = '1'; } } }else { int fg = 0; for (int i = 0; i < len; i++) { s[i] = s[0]; } s[0] = (!(s[0] - '0')) + '0'; } printf("%s\n", s); }
D Icy Land
题目链接
题意:给出一个R*C的图,图中#为普通地面,.为冰面,人在冰面会沿方向一直滑,在普通地面和边界才会停止。只有停止的地面才会被记录,求人在整个图都能停下被记录需要更改几个冰面变为普通地面。
思路:对于除边界内部需要每个地面都为普通地面才可以全被记录,而边界需要至少存在一个普通地面使得人在边界能停止。特判1、2情况。
#include<bits/stdc++.h> using namespace std; char mp[502][502]; int main(){ int r, c, i, j, ans = 0, flag = 1; scanf("%d %d",&r,&c); for(i = 0;i < r;i++){ scanf("%s",mp[i]); } if(r == 1&&c == 1){ puts("0"); } else if(r == 1){ ans = 0; for(i = 1;i < c - 1;i++){ if(mp[0][i] == '.'){ ans++; } } printf("%d\n",ans); } else if(c == 1){ ans = 0; for(i = 1;i < r - 1;i++){ if(mp[i][0] == '.'){ ans++; } } printf("%d\n",ans); } else if(r == 2){ ans = 0; for(i = 1;i < c - 1;i++){ if(mp[0][i] == '.'&&mp[1][i] == '.'){ ans++; } } printf("%d\n",ans); } else if(c == 2){ ans = 0; for(i = 1;i < r - 1;i++){ if(mp[i][0] == '.'&&mp[i][1] == '.'){ ans++; } } printf("%d\n",ans); } else{ for(i = 1;i < r - 1;i++){ for(j = 1;j < c - 1;j++){ if(mp[i][j] != '#'){ ans++; } } } for(i = 1;i < c - 1;i++){ if(mp[0][i] == '#'||mp[r - 1][i] == '#'){ flag = 0; } } for(i = 1;i < r - 1;i++){ if(mp[i][0] == '#'||mp[i][c - 1] == '#'){ flag = 0; } } printf("%d\n",ans + flag); } return 0; }
G Go Make It Complete
题目链接
题意:对于给定的无向图,你可以选择两个满足度数和大于等于k且不相连的点连接。求图变为完全图需要的最大的k。
思路:显然k具有单调性,容易想到二分。已知k,我们可以n^2枚举两个端点,存入数据结构中,连接边并更改度数。时间复杂度n^3*logn。理论不行,但可以莽过。。
#include <bits/stdc++.h> #define maxn 505 using namespace std; int n,m; int deg[maxn]; int Deg[maxn]; int g[maxn][maxn]; int G[maxn][maxn]; struct node{ int u,v; int val; }; bool check(int x){ memset(g,0,sizeof g); for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ g[i][j]=G[i][j]; } } for(int i=1;i<=n;i++){ deg[i]=Deg[i]; } while(1){ vector <node>vc; for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ if(!g[i][j]){ vc.push_back((node){i,j,deg[i]+deg[j]}); } } } int flag=0; for(int i=0;i<vc.size();i++){ if(vc[i].val>=x) { deg[vc[i].u]++; deg[vc[i].v]++; g[vc[i].u][vc[i].v]=g[vc[i].v][vc[i].u]=1; flag=1; } } if(!flag)break; } for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ if(!g[i][j]) return 0; } } return 1; } void input(){ scanf("%d%d",&n,&m); for(int i=1,u,v;i<=m;i++){ scanf("%d%d",&u,&v); Deg[u]++; Deg[v]++; G[u][v]=G[v][u]=1; } } void solve(){ int l=0,r=1000; int ans; while(l<r){ int mid=(l+r)>>1; if(check(mid)){ ans=mid; l=mid+1; } else r=mid; } printf("%d\n",ans); } int main(){ input(); solve(); } /* 7 5 1 2 2 3 3 4 4 1 2 4 */
H Lexical Sign Sequence
题目链接
题意:一个字符串,将字符串中为0的可以置为1或者-1,满足接下来k个[L,R]区间内的值和大于等于val。并要求最终字典序最小。
思路:字符串先全部位置为0的置为1。从1到n贪心,用优先队列或set存对于当前i来说的包含i的区间[l,r]中的最值。临时变量t用于赋给当前区间进入时一个基准(?)。可以判定在此区间可以减去最多的次数。时间复杂度严格O(nlogn)。
网上还有区间排序后遍历区间贪心,之前想时间复杂度不对,是卡数据莽过,后来想想好像更改的次数是确定的,可能也可以。但贪心改正确性可能也有问题(?)
#include <bits/stdc++.h> #define maxn 100005 using namespace std; int n, k; int a[maxn]; int book[maxn]; int presum[maxn]; struct SEG{ int l, r; int sum; bool operator <(const SEG temp)const{ return sum < temp.sum; } } seg[maxn]; bool cmp(SEG a,SEG b){ if(a.l!=b.l) return a.l < b.l; return a.r < b.r; } priority_queue<SEG> q; void solve(){ int ind = 1; int t = 0; for (int i = 1;i<=n;i++){ while(!q.empty()&&q.top().r<i) q.pop(); for (; ind <= k && seg[ind].l <= i;ind++){ seg[ind].sum += t; q.push(seg[ind]); } if(!book[i]){ if(q.empty()||q.top().sum+2<=t){ a[i] = -1; t-=2; } } } for (int i = 1; i <= n;i++) printf("%d ", a[i]); } void input(){ scanf("%d%d", &n, &k); for (int i = 1;i<=n;i++){ scanf("%d", &a[i]); if(a[i]==0) a[i] = 1; else book[i] = 1; presum[i] = presum[i - 1] + a[i]; } for (int i = 1; i <= k;i++){ scanf("%d%d%d", &seg[i].l, &seg[i].r, &seg[i].sum); seg[i].sum -= presum[seg[i].r] - presum[seg[i].l - 1]; } for (int i = 1; i <= k;i++){ if(seg[i].sum>0){ printf("Impossible\n"); return; } } sort(seg+1,seg+1+k,cmp); solve(); } int main(){ input(); return 0; } /* 3 2 0 0 0 1 2 2 2 3 -1 */
I Lie Detector
题目链接
题意:给出n个字符串,第i个字符串代表第i-1个字符串的真假,判定第一个字符串的真假。
思路:递推
#include<iostream> #include<cstdio> using namespace std; char ch[1002]; int main(void){ int ans = 0, t; scanf("%d",&t); while(t--){ scanf("%s",ch); if(ch[0] != 'T'){ ans ^= 1; } } if(!ans){ puts("TRUTH"); } else{ puts("LIE"); } return 0; }
J Future Generation
题目链接
题意:给定n个字符串,要求寻找n个子串,满足子串递增,且所有子串长度和最大
思路:dp
#include<bits/stdc++.h> using namespace std; const int maxn = 7e4 + 3; char s[maxn]; struct node { string str; int len; }dat[16][maxn]; int str_cnt[16]; int dp[16][maxn];// int n; int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%s", s); int le = strlen(s); int lim = 1 << le; str_cnt[i] = lim - 1; for (int j = 1; j < lim; j++) { string ans = ""; int cnt = 0; for (int k = 0; k <= le - 1; k++) { if (j&(1 << k)) { ans += s[k]; cnt++; } } dat[i][j].str = ans; dat[i][j].len = cnt; //cout << ans << "yyy" << endl; } sort(dat[i] + 1, dat[i] + lim, [&](node a, node b) { return a.str < b.str; }); } int fg = 0; for (int i = 2; i <= n; i++) { int ptr1 = 1;//last int ptr2 = 1;//NOw while (i != 2 && !dp[i - 1][ptr1]) { ptr1++; } for (; ptr1 <= str_cnt[i - 1]; ptr1++) { for (; ptr2 <= str_cnt[i];) { if (dat[i - 1][ptr1].str >= dat[i][ptr2].str) { ptr2++; } else { break; } } if (ptr2 <= str_cnt[i]) { dp[i][ptr2] = max(dp[i][ptr2], dp[i - 1][ptr1] + dat[i - 1][ptr1].len); } } for (int j = 1; j <= str_cnt[i]; j++) { dp[i][j] = max(dp[i][j], dp[i][j - 1]); //cout << i << " " << j << " : " << dp[i][j] << endl; } if (!dp[i][str_cnt[i]]) { fg = 1; break; } } if (fg)puts("-1"); else { int ans = 0; for (int i = 1; i <= str_cnt[n]; i++) { //cout << dp[n][i] << " " << endl; ans = max(ans, dp[n][i] + dat[n][i].len); } printf("%d\n", ans); } } /* 3 aaa aaa aa */
L Binary String
题目链接
题意:对于一个01串,问最小删多少个字符可以满足01组成的二进制小于等于k
思路:贪心
#include<bits/stdc++.h> using namespace std; const int maxn = 1e5 + 3; #define ll long long ll num[102]; char ch[102]; ll wei = 0, a, len; bool check(){ int flag = 0,i, now = 0, s = 0; for(i = 0;i < len;i++){ if(flag){ now++; } else{ if(num[now]){ if(ch[i] == '0'){ flag = 1; } now++; } else{ if(ch[i] == '0'){ now++; } } } if(now == wei){ return true; } } now = flag = s = 0; for(i = 0;i < wei;i++){ if(num[i]){ now++; } else{ s++; } if(now == 2){ flag = i; break; } } now = 0; for(i = 0;i < len;i++){ if(ch[i] == '0'){ now++; } if(now == s + 1){ if(len - i - 1>= wei - flag - 1){ return true; } break; } } return false; } int main() { int i; scanf("%lld %s",&a, ch); len = strlen(ch); if(!a){ wei = 1; num[0] = 0; } else{ while(a){ num[wei++] = a % 2; a /= 2; } for(i = 0;i < wei / 2;i++){ num[i] += num[wei- i - 1]; num[wei - i - 1] = num[i] - num[wei - i - 1]; num[i] -= num[wei - i - 1]; } } if(wei > len){ puts("0"); } else if(check()){ printf("%lld\n",len - wei); } else{ printf("%lld\n",len - wei + 1); } }