[acm/icpc2016香港赛区][Kattis] Slim Cut 支持撤销的动态规划(?)

来源:互联网 发布:淘宝网店赚钱 编辑:程序博客网 时间:2024/06/10 13:16

将边权从大到小排序,若一条边属于两个并查集,则看剩下的并查集划分成两个集合的中小集合最大是多少,用这两个东西来更新答案。

在同一个并查集里的点集不能被划分到两个集合中

至于判断划分成两个集合的中小集合最大是多少,如果用一个朴素的背包动态规划F[i]表示能不能凑到价值为i,那么单次操作是n^2的,总时间代价n^3,显然超时。

考虑任意两个相邻的动态规划,都是将两个元素合并成一个元素。

但是上述的动态规划显然不支持撤销

考虑记录方案数F[i] 为凑到价值i的方案数,对于最后一个物品,有转移方程

for i=n~v

F[i] += F[i-v]

显然撤销转移方程

for i=v~n

F[i] -= F[i-v]

因为这个动态规划物品的顺序是不影响最后的结果,所有对于任意物品的撤销是等价于对于最后一个物品的撤销的

这样每次撤销O(n),新增O(n)

总时间代价O(n^2)

由于方案数可能很多,对1e8+7取模,若为0则认为不存在这种方案


诶这一看就不是正解,但是跑过去了。


#include <iostream>#include <cstdio>#include <algorithm>#define N 15000#define M 30000#define mod 100000007using namespace std;typedef double db;struct Edge{ int a,b,v;}e[M];bool operator < (Edge p1,Edge p2) { return p1.v > p2.v; }int F[N],fa[N],siz[N],n,m,t,j,tot;int gf(int x) {if (fa[x] == x) return x;int t = gf(fa[x]); siz[t] += siz[x]; siz[x] = 0; return fa[x] = t;}inline void inc(int &x,int y) { x = x+y>mod ? x+y-mod : x+y; }inline void dec(int &x,int y) { x = x-y<  0 ? x-y+mod : x-y; }inline void  ut(int &x,int y) { x = max(x,y); }inline void pug(int x){ for (int i=x;i<=n;i++) dec(F[i],F[i-x]); }inline void pig(int x){ for (int i=n;i>=x;i--) inc(F[i],F[i-x]); }int main() {#ifndef ONLINE_JUDGEfreopen("3.in","r",stdin);freopen("3.out","w",stdout);#endifscanf("%d%d",&n,&m);for (int i=1;i<=n;i++) fa[i] = i , siz[i] = 1;for (int i=1;i<=m;i++) scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].v);for (int i=1;i<=m;i++) e[i].a++ , e[i].b++; sort(e+1,e+m+1); db ans = 1e9; F[0] = 1;for (int i=1;i<=n;i++)for (int j=n;j>=1;j--) inc(F[j],F[j-1]);for (int i=1;i<=m&&tot<n-1;i++) {#define p1 gf(e[i].a)#define p2 gf(e[i].b)if (p1 != p2) {int s1 = siz[p1] , s2 = siz[p2];for (t=0,j=0;j<=n;j++) if (F[j]) ut(t,min(j,n-j));siz[p2] += siz[p1]; siz[p1] = 0;fa[p1] = p2;pug(s1); pug(s2); pig(s1+s2);tot++;if (tot <= n-1) ans = min(ans , (db)e[i].v / (db)t );}}printf("%.10lf\n",ans);return 0;}


时间代价极其垃圾对拍暴力程序2^n

#include <iostream>#include <cstdio>#define N 10005using namespace std;int a[N],b[N],v[N],F[N],n,m,tot;double ans = 1e9;void dfs(int dep) {if (dep > n) {if (tot==0 || tot == n) return ;double r=-1;double q = min(tot,n-tot);for (int i=1;i<=m;i++) if (F[a[i]] ^ F[b[i]]) r=max(r,(double)v[i] / (double) q);ans = min(ans,r);return ;}F[dep] = 0; dfs(dep+1);F[dep] = 1; tot++; dfs(dep+1); tot--;}int main(){#ifndef ONLINE_JUDGEfreopen("3.in","r",stdin);freopen("3_cmp.out","w",stdout);#endifscanf("%d%d",&n,&m);for (int i=1;i<=m;i++) scanf("%d%d%d",&a[i],&b[i],&v[i]);for (int i=1;i<=m;i++) a[i]++ , b[i]++;dfs(1);printf("%.10lf\n",ans);return 0;}


造数据

造的是一个联通图,但可能有重边和自环。所幸暴力和正解都能处理这种情况,也就没去多想了。

#include <iostream>#include <cstdio>#include <ctime>#include <cstdlib>using namespace std;const int maxn = 40;const int maxm = 4000;const int v = 1e5;int main() {freopen("3.in","w",stdout);srand(time(0));int n = rand()%maxn+2 , m = rand()%maxm+n-1;printf("%d %d\n",n,m);for (int i=2;i<=n;i++) printf("%d %d %d\n",i-1,rand()%(i-1)+1-1,rand()*rand()%v+1);for (int i=1;i<=m-n+1;i++)printf("%d %d %d\n",rand()%n+1-1,rand()%n+1-1,rand()*rand()%v+1);return 0;}


0 0
原创粉丝点击