g_2 + g_3

来源:互联网 发布:程序员 博客 编辑:程序博客网 时间:2024/06/05 01:03

g_2
二分染色 + DP
染色后的题大概就是
先计算出n对数字的和sum
在n对数字中从每一对中选一个数字,然后计算这n个数的和tot
让sum-tot 和tot的差值绝对值最小
这部分可以背包dp和另一种
dp[i][j] = 1表示存在选了前i个数字中差异为j的情况
然后代码中注释写了接下来的操作=_=
注意差值为负数要修正到正数

/*poj1112*/#include <set>#include <cstring>#include <algorithm>#include <iostream>#include <cstdio>#include <cmath>#include <vector>#define  mp make_pair#define pb push_back#define  X first#define  Y secondusing namespace std;typedef long long LL;const int maxn = 105;int n, m, flag;bool vis[maxn], dp[maxn][2 * maxn];int data[maxn][2];int color[maxn];set<int> S[maxn][2];vector<int>E[maxn];struct rd {    int diff[maxn * 2];} road[maxn];void dfs(int u, int k, int t) {    if(flag == -1) return ;    color[u] = k;    for(int i = 0; i < E[u].size(); i++) {        int v = E[u][i];        if(color[v] == 0)            dfs(v, k % 2 + t, t);        else if(color[v] == k) flag = -1;    }}void solve() {    int tot = 1;    memset(vis, 0, sizeof vis);    for(int i = 1; i <= n; i++)        if(color[i] == 0) {            dfs(i, tot, tot);            tot += 2;//tot/2个连通分量        }    //cout<<color[i]<<endl;    if(flag == -1) {        puts("No solution");        return ;    }    /**        将这些分成二分图,注意有多个连通分量        然后存每个连通分量的结点,分别保存在S[][0],S[][1]中        将结点个数存在data[][]        对结点个数进行DP        DP[i][j] == 1表示选了前i个时差值为j        所以如果DP[i-1][j] == 1,DP[i][j+x-y]和DP[i][j+y-x]也为1         保存路径的时候倒过来想即可    **/    for(int i = 1; i <= n; i++) {        S[(color[i] + 1) / 2][color[i] & 1].insert(i);        //cout<<(color[i]+1)/2<<endl;    }    for(int i = 1; i <= tot / 2; i++) {        data[i][0] = S[i][0].size();        data[i][1] = S[i][1].size();        // cout<<data[i][0]<<" "<<data[i][1]<<endl;    }    dp[0][100] = 1;    for(int i = 1; i <= tot / 2; i++)        for(int j = 0; j <= 200; j++)            if(dp[i - 1][j]) {                int x = data[i][0], y = data[i][1];                dp[i][j + x - y] = 1;                road[i].diff[j + x - y] = 0;                dp[i][j + y - x] = 1;                road[i].diff[j + y - x] = 1;                //cout<<i<<": "<<j + x - y <<" "<<abs(j + y - x)<<endl;            }    int mn = 1e8, s = -1;    for(int j = 0; j <= 200; j++)        if(dp[tot / 2][j]) {            if(abs(j - 100) < mn) {                mn = abs(j - 100);                s = j;            }        }    vector<int> Q;    Q.clear();    LL cnt = 0;    for(int i = tot / 2; i >= 1; i--) {        int k = road[i].diff[s];        set<int>::iterator it;        for(it = S[i][k].begin(); it != S[i][k].end(); it++) Q.push_back(*it);        s -= data[i][k] - data[i][1 - k];    }    cout << Q.size();    memset(vis, 0, sizeof vis);    for(int i = 0; i < Q.size(); i++) {        cout << " " << Q[i];        vis[Q[i]] = 1;    }    puts("");    cout << n - Q.size();    for(int i = 1; i <= n; i++)        if(!vis[i])            cout << " " << i;    puts("");}void init() {    scanf("%d",&n);//cin会超时    flag = 1;    for(int i = 1; i <= n; i++) {        vis[i] = 1;        for(int j = 1; j <= n; j++) {            int x;            scanf("%d",&x);            if(!x) break;            vis[x] = 1;        }        for(int j = 1; j <= n; j++)            if(!vis[j]) {                E[i].push_back(j);                E[j].push_back(i);            }        memset(vis, 0, sizeof vis);    }    solve();}int main() {#ifdef LOCAL    freopen("in.txt", "r", stdin);#endif // LOCAL    /**        题意:n个人,每个人都想和某些人一起,将n个人分成两组,两组的人数差最小是多少,输出每个组的人        二分染色 + DP    **/    init();    return 0;}

g_3
第一道:给m个条件
l,r,x在[l,r]中至少选x个整数
然后问在全部区间内选的最少整数是多少

题解看代码注释部分

第二道
有n个点,
给了m条有向边,问
1.至少选几个点可以走完全部点
可以缩点(去环)后求一下有多少个入度为0的点,选这些点即可

2.至少添加多少条边使整幅图强连通
计算入度为0的点的个数a和出度为0的点个数b
将max(a,b)全部连上min(a,b)上的点,就能强连通
答案输出max(a,b)即可
不过要特判强连通分支只有一个的时候
因为这时不用连边
还有一道网络流不会,学了再补-_-

/*poj 1201*/#include <set>#include <cstring>#include <algorithm>#include <iostream>#include <cstdio>#include <cmath>#include <queue>#include <vector>#define  mp make_pair#define pb push_back#define  X first#define  Y secondusing namespace std;typedef long long LL;const int maxn = 50055;int n, m, ed, tot;struct Edge {    int v, w;    int next;} E[3 * maxn];int dis[maxn], head[maxn];bool vis[maxn];void AddEdge(int u, int v, int w) {    E[tot].v = v;    E[tot].w = w;    E[tot].next = head[u];    head[u] = tot++;}int bellman(int start) {    for(int i = ed; i <= n; i++) dis[i] = 1e8;    memset(vis, 0, sizeof vis);    dis[start] = 0;    queue<int>Q;    Q.push(start);    while(!Q.empty()) {        int x = Q.front();        Q.pop();        vis[x] = 0;        for(int e = head[x]; e != -1; e = E[e].next) {            if(dis[E[e].v] > dis[x] + E[e].w){                dis[E[e].v] = dis[x] + E[e].w;                if(!vis[E[e].v]){                    vis[E[e].v] = 1;                    Q.push(E[e].v);                }            }        }    }    return -dis[ed];}void init() {    while(scanf("%d", &m) > 0) {        memset(head, -1, sizeof head);        n = -1;        ed = 1e8;        for(int i = 0; i < m; i++) {            int l, r, w;            scanf("%d%d%d", &l, &r, &w);            n = max(n, r);            ed = min(ed, l - 1);            AddEdge(r, l - 1, -w);        }        for(int i = ed + 1; i <= n; i++) {            AddEdge(i - 1, i, 1);            AddEdge(i, i - 1, 0);        }        printf("%d\n", bellman(n));    }}int main() {#ifdef LOCAL    freopen("in.txt", "r", stdin);#endif // LOCAL    init();    /**        将[l,r]中选至少w个可以看成 c[r] - c[l-1] >= w        c[i]表示从0到i选多少个        不过这样条件还不够,所以还要再发现 c[i] - c[i-1] >= 0 && c[i]-c[i-1] <= 1        这样就转化成了差分约束        然后跑一遍SPFA,bellman-ford好像会超时。。。    **/    return 0;}
/*poj 1236*/#include <set>#include <cstring>#include <algorithm>#include <iostream>#include <cstdio>#include <cmath>#include <queue>#include <stack>#include <vector>#define  mp make_pair#define pb push_back#define  X first#define  Y secondusing namespace std;typedef long long LL;const int maxn = 105;int n, m, tot = 0, cnt = 0;bool vis[maxn];vector<int>E[maxn];int dfn[maxn], low[maxn],belong[maxn],in[maxn],out[maxn];vector<int>Q[maxn];stack<int>S;void dfs(int u) {    dfn[u] = low[u] = ++tot;    S.push(u);    vis[u] = 1;    for(int i = 0; i < (int)E[u].size(); i++) {        int v = E[u][i];        if(!dfn[v]) {            dfs(v);            low[u] = min(low[u], low[v]);        } else if(vis[v])            low[u] = min(low[u], dfn[v]);    }    if(low[u] == dfn[u]) {        int v = -1;        cnt++;        Q[cnt].clear();        while(v != u) {            v = S.top();            belong[v] = cnt;            S.pop();            vis[v] = 0;            Q[cnt].pb(v);        }    }}void init() {    while(scanf("%d", &n) > 0) {        cnt = tot = 0;        memset(vis,0,sizeof vis);        memset(dfn,0,sizeof dfn);        memset(in,0,sizeof in);        memset(out,0,sizeof out);        int v;        for(int i = 1; i <= n; i++)            while(scanf("%d", &v), v) {                E[i].push_back(v);            }        for(int i = 1; i <= n; i++)            if(!dfn[i])                dfs(i);        /*        for(int i = 0; i < cnt; i++)            for(int j = 0; j < Q[i].size(); j++)                printf("%d%c", Q[i][j], j == (Q[i].size() - 1) ?  '\n' : ' ');        */        for(int i=1;i<=n;i++){            for(int j=0;j<E[i].size();j++){                int v = belong[E[i][j]], u = belong[i];                if(u!=v){                    out[u]++;                    in[v]++;                }            }            E[i].clear();        }        int a = 0,b = 0;        for(int i=1;i<=cnt;i++){            if(!in[i])                a++;            if(!out[i])                b++;        }        if(cnt ==1) printf("1\n0\n");        else        printf("%d\n%d\n",a,max(a,b));    }}int main() {#ifdef LOCAL    freopen("in.txt", "r", stdin);#endif // LOCAL    init();    /**    **/    return 0;}
0 0
原创粉丝点击