列表

详情


NC249317. 模拟退火

描述

给定一张n个点,m条边的图,你可以进行如下两种操作:
1.选择一条边并删掉,这个操作代价为a
2.选择一个点并删掉与之关联的所有边,这个操作代价为b

你希望以最小的代价删掉图中所有的边。请你输出这个最小代价。

输入描述

第一行一个正整数T(1\le T\le 10),表示数据组数。对于每组数据:
第一行n,m,a,b(1\le n,m\le40 , 1\le a,b \le 10^7),代表图的点数和边数,和删掉边和点的代价。
接下来m行,每行两个正整数u,v(1\le u,v \le n),代表图上的一条边。

图中可能存在重边和自环。

输出描述

对于每组数据,一行一个正整数表示答案。

示例1

输入:

3
5 5 3 2
1 2
2 2
1 4
2 4
3 5
4 5 3 4
1 4
1 3
1 2
2 4
3 4
5 6 6 7
1 2
3 4
4 5
5 2
3 3
1 4

输出:

6
8
20

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++(clang++ 11.0.1) 解法, 执行用时: 1411ms, 内存消耗: 540K, 提交时间: 2023-06-28 08:33:22

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ui = unsigned int;
using ull = unsigned long long;
const ui N(41);
ui e[N][N], g[N][N];
ui n, a, b, lim, val, lef, ans;
ui cond[N], cono[N], *d(cond), *ord(cono);
inline bool cmp(const ui a, const ui b) { return d[a] < d[b]; }
void dfs(void) {
  // putc('1', stderr);
  if (!lef) {
    if (val < ans)
      ans = val;
    return;
  }
  // putc('2', stderr);
  ui sum(0);
  for (ui i(0); i < lef; i++)
    sum += d[ord[i]];
  if (val + b * lef >= ans + sum * a)
    return;
  // putc('3', stderr);
  ui pcond[N], pcono[N], *pd(d), *pord(ord), pval(val), u, v;
  d = pcond, ord = pcono;
  for (ui pl(lef); pl--;) {
    u = pord[pl];
    val = pval - pd[u] * a + b, lef = 0;
    for (ui j(0); j < pl; j++) {
      v = pord[j];
      if (pd[v] > lim + e[u][v])
        d[ord[lef++] = v] = pd[v] - e[u][v];
    }
    sort(ord, ord + lef, cmp);
    dfs();
  }
  val = pval, d = pd, ord = pord;
}
int main(void) {
  ui t;
  scanf("%u", &t);
  while (t--) {
    ui m;
    scanf("%u%u%u%u", &n, &m, &a, &b);
    lim = (b - 1) / a, ans = val = m * a, lef = 0;
    memset(e, 0, sizeof e), memset(cond, 0, sizeof cond);
    while (m--) {
      ui u, v;
      scanf("%u%u", &u, &v);
      if (u == v)
        e[u][u]++, d[u]++;
      else
        e[u][v]++, e[v][u]++, d[u]++, d[v]++;
    }
    for (ui i(1); i <= n; i++)
      if (d[i] > lim)
        ord[lef++] = i;
    sort(ord, ord + lef, cmp);
    dfs();
    printf("%u\n", ans);
  }
  return 0;
}

C++(g++ 7.5.0) 解法, 执行用时: 1497ms, 内存消耗: 23936K, 提交时间: 2023-06-27 12:36:42

#include <bits/stdc++.h>
#define int long long
#define pb push_back 
using namespace std;
mt19937 rnd(chrono::system_clock().now().time_since_epoch().count());
int gen(int l,int r) {return rnd()%(r-l+1)+l;}
const int INF=1e6+5;
int n,m,p[INF],ans,vis[INF],a,b;
vector <int> e[INF];
int check() {
    int ans=1e18,cnt=m,cnt1=0;
    for (int i=1;i<=n;i++) vis[i]=0;
    for (int i=1;i<=n;i++) {
        int d=0;
        for (int v:e[p[i]])
            if (!vis[v] || v==p[i]) d++;
        if (d*a<b) ;
        else cnt1++,cnt-=d,vis[p[i]]=1;;
        ans=min(ans,cnt1*b+cnt*a);
    }
    return ans;
}
void SA() {
    int res=1e18;
    double T=2008;
    while (T>=1e-12) {
        int x=gen(1,n),y=gen(1,n);
        swap(p[x],p[y]);
        int now=check();
        if (now<res) res=now;
        else if (exp(-(res-now)/T*RAND_MAX)>rand()) swap(p[x],p[y]);
        T*=0.997;
    }
    ans=min(res,ans);
}
void solve() {
    // double st=clock();
    cin>>n>>m>>a>>b;ans=min(n*b,m*a);
    for (int i=0;i<=n+2;i++) vector <int> ().swap(e[i]);
    for (int i=1;i<=m;i++) {
        int x=0,y=0;
        cin>>x>>y;
        e[x].pb(y);
        if (x!=y) e[y].pb(x);
    }
    int TT=30;
    for (int i=1;i<=n;i++) p[i]=i;
    while (TT--) SA();
    cout<<ans<<"\n";
    return ;
}
signed main()
{
    srand(114514);
    ios::sync_with_stdio(false);
    int t=0;cin>>t;
    while (t--) solve();
    return 0;
}

上一题