g_2 + g_3

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 second

using 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 second

using 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 second

using 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值