0%

  kruskal算法的主要思想就是从当前路径中选择一个不能构成环的最小边加入

for (int i=1;i<n;i++)
{
int mi=1000000,k;
for (int j=0;j<m;j++)
{
if ((v[e[j].x] + v[e[j].y]==1 || i==1 ) && e[j].d<mi){ mi=e[j].d; k=j; } } result.push_back(k); v[e[k].x]=1; v[e[k].y]=1; if (mi>ma) ma=mi;
}

因为此算法本质上是一个贪心策略,可以通过并查集进行优化,现将其从小到大排序

int kruskal()//返回最小生成树的和
{
int ans=0;
qsort(e,e+n,cmp);//cmp是将其按d值从小到大排序
for (int i=0;i<m;i++)//m is the number of edges
{
if (find(e[i].x)!=find(e[i].y)) {//判断边的两个端点是否在一个集合
result.push_back(i);
ans+=e[i].d;
}
}
return ans;
}

poj 1861

#include
#include
#include
#include
using namespace std;
struct P{
    int x,y,d;
};
P e[15100];
vector result;
int ma=-1;
int v[1002]={0};
int main()
{
    int n,m;
    scanf(“%d%d”,&n,&m);
    for (int i=0;i<m;i++)
        scanf(“%d%d%d”,&e[i].x,&e[i].y,&e[i].d);
    for (int i=1;i<n;i++)
    {
        int mi=1000000,k;
        for (int j=0;j<m;j++)
        {
            if ((v[e[j].x] + v[e[j].y]==1 || i==1 ) && e[j].d<mi){                 mi=e[j].d;                 k=j;             }         }         result.push_back(k);         v[e[k].x]=1;         v[e[k].y]=1;         if (mi>ma) ma=mi;
    }
    cout<<ma<<endl;
    cout<<result.size()<<endl;
    for (int i=0;i<result.size();i++)
    {
        cout<<e[result[i]].x<< “ “<<e[result[i]].y<<endl;
    }
    return 0;
}

   题意不追溯,关于匈牙利算法看:http://my.oschina.net/xuwei8091/blog/126320。 最小路径覆盖等于N-最大匹配

#include #include #include using namespace std;
int T;
int n,m;
int map[200][200];
int vis[200];
int result[200];
bool find(int s)
{
for (int i=1;i<=n;i++)
{
if (map[s][i] && !vis[i])
{
vis[i]=1;
if (result[i]==0 || find(result[i]))
{
result[i]=s;
return true;
}
}
}
return false;
}
int main()
{
scanf(“%d”,&T);
while (T–){
memset(map,0,sizeof(map));
memset(result,0,sizeof(result));
scanf(“%d%d”,&n,&m);
for (int i=0;i

    关于算法的说明,可以参看这个大牛的讲解,很详细: http://imlazy.ycool.com/post.1603708.html 直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
bool find(int s)
{
for (int i=1;i<=n;i++)//从1到n编号
{
if (map[s][i] && !visit[i])
{
    visit[i]=1;
            if (pre[i]==0 || find(pre[i]))
{
                 pre[i]=s;
return true;   
       }
}
return false;
}

int hungary()//返回最大匹配数
{
int ans=0;
for (int i=1;i<=n;i++)
    {
memset(visit,0,sizeof(visit));
        if (find(i)) ans++;
     }
return ans;
}

  好吧,具体的代码就应该是这样了,但是二分图是无向图吧。。。有向图,怎么破…

  题意就不赘诉了,青蛙约会。是一个关于欧几里得扩展的定理的题目

int gcd(int a,int b)//欧几里得求最大公约数
{
if (b==0) return a;
return gcd(b,a%b);
}

int exgcd(int a,int b,int &x,int &y)//欧几里得扩展求ax+by=gcd(a,b)的一组解
{
if (b==0) {x=1;y=0;return a;}
int r=exgcd(b,a%b,x,y);
int t=x;
x=y;
y=t-a/b*y;
return r;
}

针对本题,需要注意的是,    1.数据比较大,需要用long long 2.因为是时间,所以x不能是负数,如果出现负数怎么办呢? a*x1+b*y1=d(d=gcd(a,b)),那个转换成 a*(x1+b*n)+b*(y1-a*n)=d 最终x=(x1%b+b)%b;或者(x1+(abs([x1/b])+1)*b)%b; AC代码:

#include #include #include #include #include using namespace std;

int gcd(int a,int b)
{
if (b==0) return a;
return gcd(b,a%b);
}
long long exgcd(long long a,long long b,long long &x,long long &y)
{
if (b==0){ x=1;y=0;return a;}
long long r=exgcd(b,a%b,x,y);
long long t=x;
x=y;
y=t-(a/b)*y;
return r;
}
int main()
{
long long x,y,m,n,L;
cin>>x>>y>>m>>n>>L;
long long s=gcd(n-m,L);
if ((x-y)%s!=0){
cout<<”Impossible”<

  好吧,前两天做了一场CF,遇到一道并查集的题目,感觉并查集还需学习啊,记录一下 具体的算法如下

  1. 有father数组,全部初始化为本身
  2. 添加一个关系进来的时候,s–t,找到s的祖先a,t的祖先b,将a的father设为b
  3. 通过查找祖先,来确定是否在一个集合内
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void init(int n){//初始化,节点编号1-n
    for (int i=1;i<=n;i++) father[i]=i;
}

int find(int x)//查找祖先节点
{
while (father[x]!=x) x=father[x];
return x;
}

void add(int s,int t)//增加一个关系,改变集合
{
int a=find(s);
int b=find(t);
father[a]=b;
}

bool judge(int s,int t)//判断是不是一个集合
{
    return find(s)==find(t);
}

  原题的题意是给n个人,每个人对于控辩两方都有一个值,现在选择m个人,使得控辩两方的值的差的和最小,如果有相同的最小值,那么就选择最大的一个。 本题的关键是模型的构建,对于每个人都有两种选择,一个是选择,一个是不选择,对于相同的差值,维护一个最大的和值,换言之,就是假设差值一定,从n个人选择m个人使得和最大.可以转换为背包问题。状态转移的方程为f[i][j][k+p[i]]=max{f[i-1][j][k+p[i]],f[i-1][j-1][k]+v[i]},表示前i个人中选择j个人差的和为k的最大的和的和(p[i]是第i个人的控辩双方的值差,v[i]是第i个人的控辩双方的值和).本题的另外一个难点在于路径的记录,我们知道,动态规划的一个本质就是状态转移,也就是在当前状态记录是由哪个值推倒过来的,就可以逆序退出上个状态的值。同时通过对路径的遍历,从而可以判断当前的第i个人是否被选择过,从而达到了降维的作用。

#include #include #include #include #include using namespace std;
int a[201],b[201];
int f[30][801],path[30][801];
int p[205],v[205];
bool select(int i,int j,int k)
{
while (true)
{
if (path[i][k]<0 || i<0) break;
if (j==path[i][k]) return true;
k=k-p[path[i][k]];
i–;
}
return false;
}
int main()
{
int n,m;
int No=0;
while (scanf(“%d%d”,&n,&m)!=EOF && n+m!=0){
cout<<”Jury #”<<++No<=0){
for (int j=0;j=0 && f[m][m*20+i]>=f[m][m*20-i])
{
k=m20+i;
break;
}
if (f[m][m
20-i]>=0){
k=m20-i;
break;
}
}
int suma=0,sumb=0;
int id[300];
int sum=0;
while (true)
{
int i=path[m–][k];
if (i==-1 || m<0) break;
id[sum++]=i;
suma+=a[i];
sumb+=b[i];
k=k-p[i];
}
cout<<”Best jury has value “< #include #include #include #include using namespace std;
int a[201],b[201];
int f[30][801],path[30][801];
int p[205],v[205];
bool select(int i,int j,int k)
{
while (true)
{
if (path[i][k]<0 || i<0) break;
if (j==path[i][k]) return true;
k=k-p[path[i][k]];
i–;
}
return false;
}
int main()
{
int n,m;
int No=0;
while (scanf(“%d%d”,&n,&m)!=EOF && n+m!=0){
cout<<”Jury #”<<++No<=0){
for (int j=0;j=0 && f[m][m*20+i]>=f[m][m*20-i])
{
k=m
20+i;
break;
}
if (f[m][m20-i]>=0){
k=m
20-i;
break;
}
}
int suma=0,sumb=0;
int id[300];
int sum=0;
while (true)
{
int i=path[m–][k];
if (i==-1 || m<0) break;
id[sum++]=i;
suma+=a[i];
sumb+=b[i];
k=k-p[i];
}
cout<<”Best jury has value “<