Loading

最近做的几道题

理想路径(Ideal Path, NEERC 2010, UVa1599)

给一个n个点m条边\((2≤n≤100000,1≤m≤200000)\)的无向图,每条边上都涂有一种颜色。求从结点1到结点n的一条路径,使得经过的边数尽量少,在此前提下,经过边的颜色序列的字典序最小。一对结点间可能有多条边,一条边可能连接两个相同结点。输入保证结点1可以达到结点n。颜色为\(1~10^9\)的整数。

输入

包含多组输入,每组输入的第一行为n m,下面m行由a b c组成,ab代表图中从a到b的一条无向边,c代表这条边的颜色。

输出

对于每组输入,找出满足上面条件的一条最优路径。输出两行,第一行是路径长度,第二行是路径。

Sample Input
4 6
1 2 1
1 3 2
3 4 3
2 3 1
2 4 4
3 1 1
2 2
1 1 1
1 2 2

Sample Output
2
1 3
1
2

思路

刚拿到没啥思路,脑袋一片浆糊,看刘汝佳的思路之后有了一些想法,并写出了代码,发现TLE,根据网上AC代码改了好久还是TLE,但是说个思路。

首先题目要求要满足走的边最少的前提下,再去满足最小字典序。所以我们一定要先满足走的路径是最短的前提。

假如,我们在BFS的时候已经知道每个节点到终点最短还要走多远的话,我们就只需要去在这些同距离的邻接边中选一个满足最小字典序的边走就好了。

假设下图n=5

当前我们bfs操作所在的节点是1,假如我们现在已经知道从1到5的最少要走2条边,那么显然我们只能从3和2里选一个最小字典序,因为走4的话走的边就不是最少的。

那么既然得写代码,选2和3舍弃4的操作是咋完成的呢?

假设d包含了每个节点到终点的最短路径
在此图中d[1]=2,d[2]=1,d[3]=1,d[4]=2,d[5]=0

let adjs = 节点1的所有邻接边
for(adj : adjs){
    if(d[adj]+1 == d[1]){
        选
    }else{
        不选
    }
}

因为最短路径每一步都是最优选择,所以一定和前一个节点是差1的关系,这个可以仔细思考下。

那么万事俱备,数组d从哪来呢?上面做的所有操作都是基于d实现的。

我们可以反向bfs,也就是从终点到起点进行bfs,这样就能记录下d了。

代码

// PS:此代码不能AC,TLE。但是测了不少输入都没问题。
#include "iostream"
#include "cstdio"
#include "queue"
#include "cstring"
#include "string"

#define MAX_VERTEX 100001

using namespace std;
int n,m;
int a, b;
int c;
int s=0, t;
int vis[MAX_VERTEX];
int d[MAX_VERTEX];

struct GraphNode {
    GraphNode* next;
    int node_idx;
    int color;
}g[MAX_VERTEX];


void bfs_by_path(){
    memset(vis, 0, sizeof(vis));
    memset(d, 0, sizeof(d));
    queue<int> q;
    q.push(n);
    vis[n] = 1;
    d[n] = 0;
    while (!q.empty()) {
        int n_id = q.front(); q.pop();
        GraphNode u = g[n_id];
        GraphNode *cur = u.next;
        while (cur) {
            int v_id = cur->node_idx;
            if (!vis[v_id]) {
                d[v_id] = d[n_id] + 1;
                if(v_id==t){
                    return;
                }
                q.push(v_id);
                vis[v_id] = 1;
            }
            cur = cur->next;
        }
    }
}

void bfs_by_color(){
    printf("%d\n", d[1]); // 最短距离
    memset(vis, 0, sizeof(vis)); // 初始化
    vector<int> next{ 1 };
    vector<int> ans;
    for (int i = 0; i < d[1]; i++) { // 分层遍历
        int minColor = 0x3fffffff;
        for (int uid : next) {
            GraphNode u = g[uid];
            GraphNode *cur = u.next;
            while (cur) {
                if (d[cur->node_idx]+1==d[uid]&&cur->color <= minColor) 
                    minColor = cur->color;
                cur = cur->next;
            }
        }
        ans.push_back(minColor); 
        vector<int> next_t;
        for (int uid : next) {
			GraphNode u = g[uid];
			GraphNode *cur = u.next;
            while (cur) {
                if (d[cur->node_idx] + 1 == d[uid] && cur->color == minColor) {
                    next_t.push_back(cur->node_idx);
                    vis[cur->node_idx] = 1;
                }
                cur = cur->next;
            }
        }
        next = next_t;
    }
    for (int i = 0; i < ans.size(); i++) { 
        cout << ans[i];
        if (i == ans.size() - 1) {
            cout << endl;
        }
        else {
            cout << " ";
        }
    }
}
void insert(int s,int d,int c){
    GraphNode *dptr = new GraphNode;
    dptr->color = c;
    dptr->node_idx = d;
    dptr->next = g[s].next;
    g[s].next = dptr;
}
int main() {


    while (scanf("%d %d", &n, &m) == 2) {
        fill(g, g + MAX_VERTEX, GraphNode());
		for (int i = 0; i < m; i++) {
			scanf("%d %d %d", &a, &b,&c);
            if (a == b)continue;
			insert(a, b, c);
			insert(b, a, c);
		}
		t = n;
		bfs_by_path();
		bfs_by_color();
    }
    return 0;
}

系统依赖(System Dependencies, ACM/ICPC World Finals 1997, UVa506)

软件组件之间可能会有依赖关系,例如,TELNET和FTP都依赖于TCP/IP。你的任务是模拟安装和卸载软件组件的过程。首先是一些DEPEND指令,说明软件之间的依赖关系(保证不存在循环依赖),然后是一些INSTALL、REMOVE和LIST指令,如表6-1所示。

在INSTALL指令中提到的组件称为显式安装,这些组件必须用REMOVE指令显式删除。同样地,被这些显式安装组件所直接或间接依赖的其他组件也不能在REMOVE指令中删除。

输入

包含多组输入,每组输入由若干行指令构成,以END指令结束。每行指令包含不超过80个字符,所有组件名称都是大小写敏感的。指令名称均为大写字母。

输出

对于每行指令,echo它,就是原样输出指令。然后对于LIST,INSTALL和REMOVE,打印出他们的执行结果。

Sample Input
DEPEND   TELNET TCPIP NETCARD
DEPEND TCPIP NETCARD
DEPEND DNS TCPIP NETCARD
DEPEND  BROWSER   TCPIP  HTML
INSTALL NETCARD
INSTALL TELNET
INSTALL foo
REMOVE NETCARD
INSTALL BROWSER
INSTALL DNS
LIST
REMOVE TELNET
REMOVE NETCARD
REMOVE DNS
REMOVE NETCARD
INSTALL NETCARD
REMOVE TCPIP
REMOVE BROWSER
REMOVE TCPIP
END

Sample Output
DEPEND   TELNET TCPIP NETCARD
DEPEND TCPIP NETCARD
DEPEND DNS TCPIP NETCARD
DEPEND  BROWSER   TCPIP  HTML
INSTALL NETCARD
   Installing NETCARD
INSTALL TELNET
   Installing TCPIP
   Installing TELNET
INSTALL foo
   Installing foo
REMOVE NETCARD
   NETCARD is still needed.
INSTALL BROWSER
   Installing HTML
   Installing BROWSER
INSTALL DNS
   Installing DNS
LIST
   NETCARD
   TCPIP
   TELNET
   foo
   HTML
   BROWSER
   DNS
REMOVE TELNET
   Removing TELNET
REMOVE NETCARD
   NETCARD is still needed.
REMOVE DNS
   Removing DNS
REMOVE NETCARD
   NETCARD is still needed.
INSTALL NETCARD
   NETCARD is already installed.
REMOVE TCPIP
   TCPIP is still needed.
REMOVE BROWSER
   Removing BROWSER
   Removing TCPIP
   Removing HTML
REMOVE TCPIP
   TCPIP is not installed.
END

思路

一道模拟题,刚开始寻思挺简单的。踩了几个坑,依旧觉得没多难。测试了很多示例都没问题,丢到Vjudge上,直接RE。

主要就是要注意几点

  1. 安装一个软件不仅要先安装其依赖,还要递归安装依赖的依赖
  2. 删除软件的时候,递归删除依赖,但如果依赖是用户手动安装的,不是作为依赖安装的,那就不能删除
  3. 删除依赖和软件的时候,要注意这个软件是不是还被其他软件依赖着,如果是就不能删除
  4. LIST的输出顺序,INSTALL和REMOVE的递归打印顺序

首先,软件包都是字符串,不能直接使用,为了方便和快速,为每个软件包设定一个独立的ID。使用两个数组记录依赖关系,d[x]记录x依赖什么软件包,d2[x]记录软件包被谁依赖。这两个数组只记录依赖关系,并不参与其他逻辑。

代码

// RE代码
#include "iostream"
#include "cstdio"
#include "map"
#include "string"
#include "sstream"
#include "vector"
#include <algorithm>
#include "set"

#define NOT_INSTALLED 0
#define EXPLICIT_INSTALLED 1
#define UNEXPLICIT_INSTALLED 2


using namespace std;

vector<vector<int> > d;
vector<vector<int> > d2;
vector<int> status;
map<string, int> name_id_map;
vector<string> name_cache;
vector<int> install_list;



int n2i(string name) {
    if (!name_id_map.count(name)) {
        int r = name_cache.size();
        d.push_back(vector<int>());
        d2.push_back(vector<int>());
        status.push_back(NOT_INSTALLED);
        name_cache.push_back(name);
        name_id_map[name] = r;
        return r;
    }
    return name_id_map[name];
}
void install(string name,int p_id,int type){
    if (status[p_id] != NOT_INSTALLED) {
        if(type == EXPLICIT_INSTALLED)
			cout << "   " << name << " is already installed." << endl;
        return;
    }
    status[p_id] = type;
    for (int dpd : d[p_id]) {
		install(name_cache[dpd], dpd,UNEXPLICIT_INSTALLED);
    }
    install_list.push_back(p_id);
    cout << "   Installing " << name << endl;
}


bool isNeeded(int p_id) {
    for (int q_id:d2[p_id]) {
        if (status[q_id] != NOT_INSTALLED)return true;
    }
    return false;
}
void remove(string name,int p_id,int user_input){
    if (status[p_id] == NOT_INSTALLED) {
        if(user_input)
			cout << "   " << name << " is not installed." << endl;
        return;
    }
    // 是否有程序依赖于此程序,如果无则直接移除,如果有,不能移除
    // 移除时需要判断此程序是否是显式安装
    if (!user_input && status[p_id] == EXPLICIT_INSTALLED) return;
    if (!isNeeded(p_id)) {
        cout << "   Removing " << name << endl;
        status[p_id] = NOT_INSTALLED;
        install_list.erase(find(install_list.begin(), install_list.end(), p_id));
        for (int dpd: d[p_id]) {
			remove(name_cache[dpd], dpd,0);
        }
    }
    else {
        if(user_input)
			cout << "   " << name << " is still needed." << endl;
    }
}

int main() {
    int p_id;
    string line,cmd,op1,op2;
    while (getline(cin,line)) {
        cout << line << endl;
        if (line[0]=='E') {
            d.clear(); d2.clear(); status.clear();
            name_id_map.clear(); name_cache.clear(); install_list.clear();
            continue;
        }
        stringstream ss(line);
        cmd; ss >> cmd;
        if (cmd[0] == 'L') {
            for (int i:install_list) {
                cout << "   " << name_cache[i] << endl;
            }
        }else {
            ss >> op1;
			if (cmd[0]=='D') {
				while (ss >> op2) {
					d[n2i(op1)].push_back(n2i(op2));
					d2[n2i(op2)].push_back(n2i(op1));
				}
			}
			else if (cmd[0]=='I') {
				p_id = n2i(op1);
				install(op1, p_id, EXPLICIT_INSTALLED);
			}
			else if (cmd[0]=='R') {
				p_id = n2i(op1);
				remove(op1, p_id,1);
			}
        }
    }
    return 0;
}

UVa 673 Parentheses Balance

输入一个包含“( )”和“[ ]”的括号序列,判断是否合法。具体规则如下:

  • 空串合法。
  • 如果A和B都合法,则AB合法。
  • 如果A合法则(A)和[A]都合法。

输入

第一行输入n,下面n行输入括号组合。

输出

如果该行是合法的,输出Yes否则输出No

Sample Input
3
([])
(([()])))
([()[]()])()

Sample Output
Yes
No
Yes

思路

这题简单,栈。就是代码有点丑,我也不想改了。

代码

// AC
#include "iostream"
#include "cstring"
#include "stack"
#include "cstdio"
//#include "cstdlib"

#define WRITE(a) printf(a)
//#define WRITE(a) fprintf(fp,a);
#define MAX 129

using namespace std;

char str[MAX];

//FILE* fp;
int main() {
    //fp = fopen("a.txt", "w");
    int n;
    scanf("%d", &n);
	getchar();
    for (int i = 0; i < n; i++) {
        cin.getline(str, MAX);
        int j;
		stack<char> stk;
		int is_vaild = 1;
        for (j = 0; str[j]; j++) {
            if (str[j] == '(' || str[j] == '[') {
                stk.push(str[j]);
                continue;
            }
            if ((str[j] == ')' || str[j] == ']') && stk.empty()) {
                is_vaild = 0;
                break;
            }
            if (str[j] == ')' && stk.top() != '(') {
                is_vaild = 0;
                break;
            }
            if (str[j] == ']' && stk.top() != '[') {
                is_vaild = 0;
                break;
            }
            stk.pop();
        }
        // 如果读取到尾了并且stk为空
        if (!str[j] && stk.empty())
            is_vaild = 1;
        else is_vaild = 0;
        if (is_vaild)
            WRITE("Yes");
        else
            WRITE("No");
        WRITE("\n");
    }
    return 0;
}

UVa 712 S-Trees

给出一棵满二叉树,每一层代表一个01变量,取0时往左走,取1时往右走。

给出所有叶子的值以及一些查询,求每个查询到达的叶子的值。例如,有4个查询:000、010、111、110,则输出应为0011。

输入

包含多组输入,每组输入由n开始,接下来一行是n个节点。代表二叉树的每一层。然后一行是叶子节点的编号。然后是m,下面m行是VVS,就是查询,每次查询你需要从根节点开始,0往左,1往右。走了n次后,到达叶子节点,输出你到达的叶子节点的值。

注意:VVS中的第i步和xi的顺序一致,如果不考虑这个会走错

假如x2在根节点,那么对于输入010你应该先往右走,就是找VVS中的第二步,而第二步是1。

输出

对于每个示例,输出第一行S-Tree #j:,j是当前示例编号,然后下一行是m个VVS走到的叶子节点的顺序输出。

Sample Input
3
x1  x2  x3
00000111
4
000
010
111
110
3
x3  x1  x2
00010011
4
000
010
111
110
0

Sample Output
S-Tree  #1:
0011

S-Tree  #2:
0011

思路

也不难,可以直接模拟二叉树。但是没必要。

想想满二叉树如果按层序排列的话是什么样的?

这样的:

每一层都按顺序排列。对于当前节点i,i*2就是左子,i*2+1就是右子。

那么对于题目中最后的一排叶子,一定是2^n个,并且下标是从2^n开始。我们使用一个2^n的数组就能记录最后一排叶子。

然后我们假设n=3,对于一组VVS,比如010,当前xi顺序为x1 x2 x3也就是不用考虑顺序,我们从根节点开始走这个VVS。

当前节点根节点 1 
取VVS第一个 0 
走到左子
当前节点1*2 = 3
取VVS第二个 1
走到右子
当前节点3*2+1=6
取VVS第三个 0
走到左子
当前节点6*2=12

因为最后一排下标从`2^n`开始,所以12-2^3 = 4
那它就会去记录最后一排叶子的数组中找第4个元素
对于此次VVS,走到了第4个叶子元素上

需要注意的点

  • 顺序!!顺序!!VVS的顺序!!
  • 上面的下标很理想化,但是代码中会有些减一的操作,别忘了,或者定义的时候就从1开始,0不用。
  • 注意换行符用getchar取走

代码

// AC
#include "iostream"
#include "cstdio"
#include "string"
#include "cmath"
#include "vector"
#define MAX 128
#define MAXN 7

using namespace std;

int t[MAX];

int left(int i) {
    return i * 2;
}
int right(int i) {
    return i * 2 + 1;
}
// 获取一位
int get_1bit(){
	char c;
	scanf("%c", &c);
    return c == '0' ? 0 : 1;
}
int main() {
    int n,m,c=0;
    string tmp;
    while (scanf("%d", &n) != EOF && n!=0) {
        c++; // Case Number
        getchar();
        int order[MAXN]; // 因为顺序是按照xi的排列来的,所以记录下顺序
        for (int i = 0; i < n; i++) {
            cin >> tmp;
            order[i] = stoi(tmp.substr(1))-1;
        }
        getchar();


        // 记录最底层的终端节点
        int t_num = pow(2, n);
        for (int i = 0; i < t_num; i++) 
            t[i] = get_1bit();

        scanf("%d", &m);
        vector<int> ans; // 答案
        int vvs[MAXN]; // vvs
        // 这里应该按order中的顺序遍历 
        for (int i = 0; i < m; i++) {
            getchar();
			int s = 1;
            for (int j = 0; j < n; j++)
                vvs[j] = get_1bit();

            for (int j = 0; j < n; j++) {
				s = vvs[order[j]] ? right(s) : left(s);
            }
            ans.push_back(t[s - t_num]);
        }
        printf("S-Tree #%d:\n", c);
        for (int i : ans) {
            printf("%d", i);
        }
        printf("\n\n");
    }
    return 0;
}

参考

posted @ 2020-11-15 20:19  yudoge  阅读(154)  评论(0编辑  收藏  举报