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