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);
    }
}